递归删除JavaScript对象中的空值

28

我有一个 JSON 对象,在一些操作(例如删除某些部分)之后,我将其打印出来,除了有一些 null 值,一切看起来都很好。我该如何移除这些值?

我使用 JSON.stringify(obj, null, 2) 方法进行打印,并且以下是它的样子:

{
    "store": {
        "book": [
             null,
             {
                 "category": "fiction",
                 "author": "Evelyn Waugh",
                 "title": "Sword of Honour",
                 "price": 12.99
             },
             null,
             {
                  "category": "fiction",
                  "author": "J. R. R. Tolkien",
                  "title": "The Lord of the Rings",
                  "isbn": "0-395-19395-8",
                  "price": 22.99
             }
        ],
        "bicycle": {
             "color": "red",
             null,
             "price": 19.95
        }
    }
}

我希望它更加简洁,非常干净(删除额外的3个null值):

{
    "store": {
        "book": [
             {
                 "category": "fiction",
                 "author": "Evelyn Waugh",
                 "title": "Sword of Honour",
                 "price": 12.99
             },
             {
                  "category": "fiction",
                  "author": "J. R. R. Tolkien",
                  "title": "The Lord of the Rings",
                  "isbn": "0-395-19395-8",
                  "price": 22.99
             }
        ],
        "bicycle": {
             "color": "red",
             "price": 19.95
        }
    }
}

5
因为存在null,你的“bicycle”语法无效。 - Joe Enos
1
我想知道哪个JSON.stringify的实现生成了无效的JSON。 - James M
忽略自行车元素,你在这里是正确的,而我的错误。 - lauxp
这不是重复的,因为它具有递归方面。 - Phrogz
8个回答

53

我曾经遇到过类似的问题,不仅要删除null值,还要递归地检查嵌套对象和数组,删除undefinedNaN空字符串空数组空对象

以下函数使用了Lo-Dash:

function pruneEmpty(obj) {
  return function prune(current) {
    _.forOwn(current, function (value, key) {
      if (_.isUndefined(value) || _.isNull(value) || _.isNaN(value) ||
        (_.isString(value) && _.isEmpty(value)) ||
        (_.isObject(value) && _.isEmpty(prune(value)))) {

        delete current[key];
      }
    });
    // remove any leftover undefined values from the delete 
    // operation on an array
    if (_.isArray(current)) _.pull(current, undefined);

    return current;

  }(_.cloneDeep(obj));  // Do not modify the original object, create a clone instead
}
例如,如果您使用以下输入对象调用该方法:
var dirty = {
  key1: 'AAA',
  key2: {
    key21: 'BBB'
  },
  key3: {
    key31: true,
    key32: false
  },
  key4: {
    key41: undefined,
    key42: null,
    key43: [],
    key44: {},
    key45: {
      key451: NaN,
      key452: {
        key4521: {}
      },
      key453: [ {foo: {}, bar:''}, NaN, null, undefined ]
    },
    key46: ''
  },
  key5: {
    key51: 1,
    key52: '  ',
    key53: [1, '2', {}, []],
    key54: [{ foo: { bar: true, baz: null }}, { foo: { bar: '', baz: 0 }}]
  },
  key6: function () {}
};
它会递归地丢弃所有“不良”值,最终仅保留携带一些信息的值。
var clean = pruneEmpty(dirty);
console.log(JSON.stringify(clean, null, 2));

{
  key1: 'AAA',
  key2: {
    key21: 'BBB'
  },
  key3: {
    key31: true,
    key32: false
  },
  key5: {
    key51: 1,
    key52: '  ',
    key53: [1, '2'],
    key54: [{ foo: { bar: true }}, { foo: { baz: 0 }}]
  }
};

希望这能有所帮助!


2
最佳答案之一...(y) - Ankur Verma
1
不错!然而,这似乎正在过滤日期,这可能不是我们想要的。在我们delete current[key]之前,我们应该检查!_.isDate(value)吗? - Zuabi
或者,我们可以利用_.isPlainObject(value),但在这种情况下,我们需要更明确地处理数组。 - Zuabi
这种方法比起我那个没用递归的暴力方法更短。我还加了一个条件来去除错误值,例如我在 .forOwn if 语句中添加了两个条件(如果 value === false || value === 'false' || ......)。谢谢! - JesseBoyd

18
// Iterate the array from back to front, removing null entries
for (var i=obj.store.book.length;i--;){
  if (obj.store.book[i]===null) obj.store.book.splice(i,1);
}

如果您想要递归地从对象和数组中删除所有null值:

// Compact arrays with null entries; delete keys from objects with null value
function removeNulls(obj){
  var isArray = obj instanceof Array;
  for (var k in obj){
    if (obj[k]===null) isArray ? obj.splice(k,1) : delete obj[k];
    else if (typeof obj[k]=="object") removeNulls(obj[k]);
  }
}

实际应用场景:

var o = {
  "store": {
    "book": [
       null,
       {
         "category": "fiction",
         "author": "Evelyn Waugh",
         "title": "Sword of Honour",
         "price": 12.99
       },
       null,
       {
          "category": "fiction",
          "author": "J. R. R. Tolkien",
          "title": "The Lord of the Rings",
          "isbn": "0-395-19395-8",
          "price": 22.99
       }
    ],
    "bicycle": {
       "color": "red",
       "bad": null,
       "price": 19.95
    }
  }
}

removeNulls(o);

console.log(JSON.stringify(o,null,2));
// {
//   "store": {
//     "book": [
//       {
//         "category": "fiction",
//         "author": "Evelyn Waugh",
//         "title": "Sword of Honour",
//         "price": 12.99
//       },
//       {
//         "category": "fiction",
//         "author": "J. R. R. Tolkien",
//         "title": "The Lord of the Rings",
//         "isbn": "0-395-19395-8",
//         "price": 22.99
//       }
//     ],
//     "bicycle": {
//       "color": "red",
//       "price": 19.95
//     }
//   }
// }

如何递归迭代对象以应用此例程?考虑到我对对象的结构一无所知,store.book只是我在这里写的一个示例..我是一个完全新手的Javascript程序员。 - lauxp
我已经添加了一个实现,可以递归地遍历对象/数组并删除null值。 - Phrogz
谢谢,不知道为什么它从未进入这个“if”分支:if (obj[k]===null),嗯... - lauxp
1
很好的例子!对我来说唯一的问题是.splice()会删除当前索引,因此会跳过它右边的一个。因此,我进行了修改:for (var i = Object.keys(obj).length; i > 0; i--){ var key = Object.keys(obj)[i]; if(obj[key] === null ) { obj.splice(key,1); } else if (typeof obj[key]=="object") { this.cleanArray(obj[key]); } } return obj;通过这种修改,列表还可以删除彼此相邻的项目 var o = { "store": { "book": [ null, null, null]}}; - Lex van Buiten
1
第二个代码片段是不正确的,例如在 removeNulls([1,2,3,null,null,4,5]) 上会失败,并返回 [1,2,3,null,4,5] 而不是删除两个 null。 - Hjulle
显示剩余7条评论

11
我们可以使用JSON.stringify和JSON.parse一起递归地从对象中删除空白属性。

我们可以使用JSON.stringify和JSON.parse一起递归地从对象中删除空白属性。

jsObject = JSON.parse(JSON.stringify(jsObject), (key, value) => {
               if (value == null || value == '' || value == [] || value == {})
                   return undefined;
               return value;
           });

4
以下是对@Phrogz答案的修改。如果book[3]也为空,由于数组的长度在最后一次迭代时小于k,所以给定的答案不会移除最后一个null。
以下方法通过对数组执行第二次调用来实现:
function removeNulls(obj) {
  var isArray = obj instanceof Array;
  for (var k in obj) {
    if (obj[k] === null) isArray ? obj.splice(k, 1) : delete obj[k];
    else if (typeof obj[k] == "object") removeNulls(obj[k]);
    if (isArray && obj.length == k) removeNulls(obj);
  }
  return obj;
}

4

修复你的book数组很容易 - 你只需要过滤掉空值。最直接的方法可能是构建一个新数组并重新赋值:

var temp = [];
var i;
for (i = 0; i < obj.store.book.length; ++i) {
    if (obj.store.book[i] != null) {
        temp.push(obj.store.book[i]);
    }
}
obj.store.book = temp;

我确信有很多其他方法,比如使用jQuery或filter函数(我相信在旧版浏览器中不可用)。你还可以循环遍历数组,并将null剔除。但我发现这种方法最容易阅读


3
请使用此npm包。
npm i --save nnjson
var nnjson = require('nnjson');
user = {
  a: null,
  b: 'hello',
  c: {
    c1: 'world',
    c2: null
  }
}
var newUser = nnjson.removeNull(user);
console.log (newUser)

结果

{
  b: 'hello',
  c: {
    c1: 'world'
  }
}

1

如何删除你的代码块?

使用delete运算符删除数组元素会在数组中留下空洞。相反,您应该使用Array.splice来正确地从数组中删除元素。


0

我在这里使用以下代码:

JavaScript中删除数组中的空元素

然后你可以像这样调用它:

JSON.stringify(obj.clean(null), null, 2)

你需要修改代码以使其适用于对象(或在对象内部使用原始代码)


是的,这就是为什么我说要么修改它,要么在对象内部运行它。 - exussum

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