2

I'm trying to recursively delete null values in a JSON object and all Subobjects. If the subobjects keys are all deleted, then I want that subobject to be deleted as well.

ie.

x = {
  "applicant": {
    'first_name': null,
    'last_name': null,
    'employment_type': null
  },
  'phone': 1123123,
  'branch': null,
  'industry': {
    'id': 1,
    'name': null
  },
  "status": "333"
}

should turn into this:

x = {
    'phone': 1123123,
    'industry': {
         "id": 1
     },
     "status": "333"
    }

Here is the function that I wrote to delete all keys with null values:

function delKeys(app){
  for(key in app){
    if(app[key] !== null && typeof(app[key]) === 'object'){
      delKeys(app[key])
    } 
    if(app[key] === null){
      delete app[key]
      }
  }

But this doesn't delete the parent key with no children:

so instead of the result above, I get this:

x = {
    "applicant":{},
    "phone":1123123,
    "industry":{
       'id': 1
     }
     "status": "333"
     }

As you can see, it doesn't delete the applicant key. How would I check for that within the function? or does it need to be written in a separate function that I call AFTER calling delKeys()?

Also, does anyone see this hitting maximum recursion depth? I've tried with bigger JSON objects and it seems to be hitting max recursion depth. I would really appreciate help with debugging that

thank you.

user125535
  • 240
  • 4
  • 15

4 Answers4

6

You need to check if app[key] has keys after you delete null keys.

const x = {
  "applicant": {
    'first_name': null,
    'last_name': null,
    'employment_type': null
  },
  'phone': 1123123,
  'branch': null,
  'industry': {
    'id': 1,
    'name': null
  },
  "status": "333"
}

function isEmpty(obj) {
  for(var key in obj) return false;

  return true
}

function delKeys(app){
  for(var key in app){
    if(app[key] !== null && typeof(app[key]) === 'object'){
      delKeys(app[key])

      if(isEmpty(app[key])) {
        delete app[key]
      }
    } 
    if(app[key] === null){
      delete app[key]
    }
  }
}

delKeys(x)

console.log(x)
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
  • hey thanks this worked. One follow up question though. During the for loop for(var key in app), I removed the "var" keyword and it did not remove the empty object. But when I added it back, it removed the empty object. Why is that? – user125535 Jul 04 '17 at 17:04
  • @user125535 Using undeclared variables in non strict mode defines global variable. Since global scope is well global it creates various name conflicts and affects functions that use the same name. – Yury Tarabanko Jul 04 '17 at 17:08
4

There's a simple solution with JSON.parse' reviver function. This function is executed for every element, and if it returns undefined the element is deleted from object. You can check if value is empty and return undefined in this function, and avoid any recursive functions:

const a = {"applicant":{"first_name":null,"last_name":null,"employment_type":null},"phone":1123123,"branch":null,"industry":{"id":1,"name":null},"status":"333"};

const res = JSON.parse(JSON.stringify(a), (k, v) => {
  return (v === null // delete null values
    || (Array.isArray(v) && v.length === 0) // delete empty arrays
    || (typeof v === 'object' && Object.keys(v).length === 0)) // delete empty objects
      ? undefined : v // else return the value
});

console.log(res)
Egor Stambakio
  • 17,836
  • 5
  • 33
  • 35
1

After

delKeys(app[key]);

you should

delete app[key];

app[key] is an empty object, not === null, so it won't get picked up by the following if block.

James
  • 20,957
  • 5
  • 26
  • 41
1

Use Object.keys().length to check if the object is empty or not

function delKeys(app){
  for(key in app){
    if(app[key] !== null && typeof(app[key]) === 'object'){
       //extra check for an empty object.
       if(Object.keys(app[key]).length === 0){
         delete app[key]
       }
       else{
          delKeys(app[key])
       }
    } 
    if(app[key] === null){
      delete app[key]
    }
}
Sudhansu Choudhary
  • 3,322
  • 3
  • 19
  • 30