28

I have a JSON obj, after some operations (like delete some pieces), I print it and everything looks good except that I have some null values. How do I remove these?

I use JSON.stringify(obj, null, 2) method to print, and here is what it looks like:

{
    "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
        }
    }
}

I want it to be much compact and pretty clean(remove the extra 3 null values):

{
    "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
        }
    }
}
Phrogz
  • 296,393
  • 112
  • 651
  • 745
lauxp
  • 281
  • 1
  • 3
  • 4

8 Answers8

52

I had to solve a similar problem, however I wanted to remove not only null values but also undefined, NaN, empty String, empty array and empty object values, recursively, by inspecting nested objects and also nested arrays.

The following function is using 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
}

For example, if you invoke the method with the following input object:

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 () {}
};

It'll recursively discard all the "bad" values, keeping in the end only the ones that carry some information.

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 }}]
  }
};

Hope it helps!

jgonian
  • 2,031
  • 1
  • 18
  • 15
  • 2
    One of the best answers...(y) – Ankur Verma Nov 09 '16 at 11:01
  • 1
    Nice! However, this seems to be filtering out dates, which is probably not what we want. Should we check for `!_.isDate(value)` as well, before we `delete current[key]`? – Zuabi Mar 15 '21 at 11:09
  • Alternatively, we might make use of `_.isPlainObject(value)`, we would need to handle arrays more explicitly in that case. – Zuabi Mar 15 '21 at 11:31
  • this is so much shorter than my brute force non-recursive method. i also added a condition to remove false values for example i just added 2 more conditions in the .forOwn if statement ( if value === false || value === 'false' || ...... ty! – JesseBoyd May 24 '21 at 17:10
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);
}

If you want to remove all null values recursively from both objects and arrays:

// 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]);
  }
}

Seen in action:

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
//     }
//   }
// }
Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • how to recursively iterate a object to apply this routine? consider i have no idea of the structure of the object, the store.book is just a example i wrote here.. i'm a new Javascript guy totally – lauxp Aug 29 '13 at 16:14
  • I've added an implementation that will recursively crawl objects/arrays and remove `null` values. – Phrogz Aug 29 '13 at 16:21
  • thanks, don't know why it never fall into this 'if' branch: if (obj[k]===null), hm.. – lauxp Aug 29 '13 at 17:12
  • @user2729847 Is it the string `"null"`? Is it actually the value `undefined`? You could change the `===null` to just `==null` to be more permissive. – Phrogz Aug 29 '13 at 17:20
  • the object we're talking here is thru delete this way: var action = "delete o['store']['book'][0]"; eval(action); so the array hole here should be null, weird.. – lauxp Aug 29 '13 at 17:29
  • Time for you to learn how to [debug scripts](https://developers.google.com/chrome-developer-tools/docs/javascript-debugging). You can set a breakpoint and step through the code, seeing what the name of the key is and the value in the array. However, in this case, `delete` sets a key to `undefined`, NOT to `null`. – Phrogz Aug 29 '13 at 17:34
  • good suggestion and appreciated, here if i change "delete o['store']['book'][0]" to "o['store']['book'][0] = null", it works. so is this a good way to go? – lauxp Aug 29 '13 at 18:44
  • No, if you want to remove `undefined` values as well as `null` values, simply change `===null` to `==null` and it will work. – Phrogz Aug 29 '13 at 19:51
  • appear not right, in this case, delete o['store']['book'][0] and deleteo['store']['book'][2], then the value of these two holes are neither null nor undefined. and ==null can't catch it. – lauxp Aug 30 '13 at 21:02
  • aha, the problem is here obj.splice(k,1), the argument should be index rather than k, thanks for the help! – lauxp Aug 30 '13 at 21:31
  • 1
    Great example! The only problem for me was that `.splice()` removes the current index and therefore skips the one right after it. Therefore i modified it to: `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;` With this modification the list also deletes items right after each other `var o = { "store": { "book": [ null, null, null]}};` – Lex van Buiten Jan 28 '16 at 10:58
  • 1
    The second code snippet is incorrect and will fail on for example `removeNulls([1,2,3,null,null,4,5])` and return `[1,2,3,null,4,5]` instead of removing both nulls. – Hjulle Dec 02 '17 at 22:52
11

We can use JSON.stringify and JSON.parse together to recursively remove blank attributes from an object.

jsObject = JSON.parse(JSON.stringify(jsObject), (key, value) => {
               if (value == null || value == '' || value == [] || value == {})
                   return undefined;
               return value;
           });
Vardaman PK
  • 313
  • 2
  • 7
4

Fixing your book array is easy enough - you just have to filter out the nulls. The most straightforward way would probably be building a new array and reassigning it:

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;

I'm sure there are plenty of other ways, like using jQuery, or the filter function (which I believe is not available in older browsers). You could also loop through the array and splice out the nulls. I just find this way the easiest to read.

Joe Enos
  • 39,478
  • 11
  • 80
  • 136
4

The following is a modification to the answer by @Phrogz. If book[3] was also null, the answer given would not remove the last null because the array's length would be less than k in last loop's iteration.

The following would work by performing a second call to the array:

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;
}
cortopy
  • 2,758
  • 2
  • 25
  • 31
3

Please use this npm package

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)

result

{
  b: 'hello',
  c: {
    c1: 'world'
  }
}
Nomura Nori
  • 4,689
  • 8
  • 47
  • 85
1

How do you deletes your pieces ?

Delete an array element with the delete operator leaves a hole in the array. Instead, you should use Array.splice which can remove properly an element from array.

BAK
  • 972
  • 8
  • 23
0

I use the code here

Remove empty elements from an array in Javascript

then you could call it like

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

You would need to modify the code to work with objects too (or use the code as is inside the objects)

Community
  • 1
  • 1
exussum
  • 18,275
  • 8
  • 32
  • 65