如何深度删除对象中的null值、空对象和空数组

3

我有一个看起来像这样的对象:

var myObject = { a: { b: [{}], c: [{}, {d: 2}], e: 2, f: {} }, g:{}, h:[], i: [null, 2] }

我想要删除空值和空数组、空对象,使其看起来像这样:

我希望您能够从中删除空值和空对象(包括数组和对象),使其看起来如下:

{ a: {c: [ {d: 2} ], e: 2 }, i: [ 2 ] }

该函数应该移除空值,空对象和空数组。有没有一种优雅的方法来完成它?

可能使用正则表达式是一种不好的做法。 - Sphinx
3
我不理解你期望得到的输出。如果输入对象中a.c是一个数组,为什么非空数组没有被保留,即{ a: { c: [{ d: 2 }], e: 2}, i: [2] }?为什么[null, 2]i数组会消失,然后出现在g的属性中?为什么输出中有两个嵌套的c属性,而输入中只有一个c属性? - CertainPerformance
在您的答案中运行代码会产生 { a: { c: [{ d: 2 }], e: 2}, i: [2] },这与问题描述中所期望的一致,因此我假设问题中发布的期望输出只是打字错误? - CertainPerformance
我怀疑使用ES6语法可能有一个非常优雅的答案,只需要3或4行代码。希望有人能提供。 - ESR
@EdmundReed 我相信任何解决方案都需要手动递归,显式测试对象与数组与基元(和null),以及显式检查剩余真实键的数量 - 这太多了,只能用几行完成。我的答案使用了15个行,它可以被压缩成更少的行,但这会损害可读性,而可读性比代码长度更重要。 - CertainPerformance
我的期望输出中有一个错别字,我进行了更正。 - JLavoie
3个回答

1

这是一个递归清理对象的函数。它将深度遍历所有属性并删除null值、null数组和null对象:

cleanUpObject(jsonObject: object): object {

    Object.keys(jsonObject).forEach(function (key, index) {
        const currentObj = jsonObject[key]

        if (_.isNull(currentObj)) {
            delete jsonObject[key]
        } else if (_.isObject(currentObj)) {
            if (_.isArray(currentObj)) {
                if (!currentObj.length) {
                    delete jsonObject[key]
                } else {
                    const cleanupArrayObj = []
                    for (const obj of currentObj) {
                        if (!_.isNull(obj)) {
                            const cleanObj = this.cleanUpJson(obj)
                            if (!_.isEmpty(cleanObj)) {
                                cleanupArrayObj.push(cleanObj)
                            }
                        }
                    }
                    if (!cleanupArrayObj.length) {
                        delete jsonObject[key]
                    } else {
                        jsonObject[key] = cleanupArrayObj
                    }
                }
            } else {
                if (_.isEmpty(Object.keys(jsonObject[key]))) {
                    delete jsonObject[key]
                } else {
                    jsonObject[key] = this.cleanUpJson(currentObj)

                    if (_.isEmpty(Object.keys(jsonObject[key]))) {
                        delete jsonObject[key]
                    }
                }
            }
        }
    }, this)

    return jsonObject
}

0
我们不知道你所说的“clean”是什么意思,但根据我理解,你想要移除所有的空和null值。这个算法很简单:递归地检查并删除任何空/ null值(这些值将会被递归地检查)。

function clean(obj) {
  // clean array
  if (Array.isArray(obj)) {
    for (let i=0; i<obj.length; i++) {
      if (isNothing(obj[i])) obj.splice(i, 1);  // remove value if falsy
      else if (typeof obj[i] === 'object') clean(obj[i]); // recurse if it's a truthy object
    }
    
  // clean other object
  } else {
    for (let prop in obj) {
      if (!obj.hasOwnProperty(prop)) continue;
      if (isNothing(obj[prop])) delete obj[prop]; // remove value if falsy
      else if (typeof obj[prop] === 'object') clean(obj[prop]); // recurse if it's a truthy object
    }
  }
}

// Recursively check for populated or nonnull content. If none found, return `true`. Recursive so [{}] will be treated as empty.
function isNothing(item) {
  // null / undefined
  if (item == null) return true;
  
  // deep object falsiness
  if (typeof item === 'object') {
    if (Array.isArray(item)) {
      // array -> check for populated/nonnull value
      for (let i=0; i<item.length; i++) {
        if (!isNothing(item[i])) return false;
      }
      return true;
    }
    // other object -> check for populated/nonnull value
    for (let prop in item) {
      if (!item.hasOwnProperty(prop)) continue;
      if (!isNothing(item[prop])) return false;
    }
    return true;
  }
  return false;
}

var myObject = { a: { b: [{}], c: [{}, {d: 2}], e: 2, f: {} }, g:{}, h:[], i: [null, 2] };

console.log("Before: " + JSON.stringify(myObject));
clean(myObject);
console.log("After: " + JSON.stringify(myObject));


-1
为了减少重复的代码,一种选择是定义一个函数(我们称之为itemToBool),该函数可以确定传递给它的通用值是否为真,或者在值为数组或对象时递归地确定其真实性。然后,在传递原始对象的函数中(或者在递归传递对象或数组的函数中),每当需要验证值时,都可以调用itemToBool函数。
对于数组,通过mapitemToBool,然后通过布尔值filter。对于对象,将对象的entries缩减为另一个对象:将对象的每个值通过itemToBool递归转换(如果值是数组或对象,则进行转换),如果转换后的值具有任何键(或为真实的基元),则将其分配给累加器。无需依赖库:

var myObject = {
  a: {
    b: [{}],
    c: [{}, {
      d: 2
    }],
    e: 2,
    f: {}
  },
  g: {},
  h: [],
  i: [null, 2]
};

// Returns a falsey value if the item is falsey,
// or if the deep cleaned array or object is empty:
const itemToBool = item => {
  if (typeof item !== 'object' || item === null) return item;
  const cleanedItem = clean(item);
  return Object.keys(cleanedItem).length !== 0 && cleanedItem;
};

const clean = obj => {
  if (Array.isArray(obj)) {
    const newArr = obj.map(itemToBool).filter(Boolean);
    return newArr.length && newArr;
  }
  const newObj = Object.entries(obj).reduce((a, [key, val]) => {
    const newVal = itemToBool(val);
    if (newVal) a[key] = newVal;
    return a;
  }, {});
  return Object.keys(newObj).length > 0 && newObj;
};

console.log(clean(myObject));

嗯...你也可以将检查键数的操作抽象成一个函数:

var myObject={a:{b:[{}],c:[{},{d:2}],e:2,f:{}},g:{},h:[],i:[null,2]}

// Returns the object / array if it has at least one key, else returns false:
const validObj = obj => Object.keys(obj).length && obj;
const itemToBool = item => (
  typeof item !== 'object' || item === null
  ? item
  : validObj(clean(item))
);
const clean = obj => validObj(
  Array.isArray(obj)
  ? obj.map(itemToBool).filter(Boolean)
  : Object.entries(obj).reduce((a, [key, val]) => {
      const newVal = itemToBool(val);
      if (newVal) a[key] = newVal;
      return a;
    }, {})
);

console.log(clean(myObject));


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接