14

I am trying to remove empty objects inside an object, here is an example with the expected output:

var object = {
    a: {
        b: 1,
        c: {
            a: 1,
            d: {},
            e: {
              f: {} 
            }
        }
    },
    b: {}
}


var expectedResult = {
    a: {
        b: 1,
        c: {
            a: 1,
        }
    }
}

I tried using some examples from other StackOverflow questions, however those are just for one level objects.

2
  • Use a recursive function. If you have a function that receives an object and iterates it looking for empty objects, and removes them, then when it comes to an object that isn't empty, simply call the same function with that inner object as the argument. Commented Mar 11, 2017 at 14:02
  • @squint please give me an example, i am still starter in javascript, trying to catch up.l Commented Mar 11, 2017 at 14:20

4 Answers 4

29

Basic function that removes empty objects

First start with a function that only works with a single level of nesting.

This function removes all properties that reference an empty object:

function clearEmpties(o) {
  for (var k in o) {
    if (!o[k] || typeof o[k] !== "object") {
      continue // If null or not an object, skip to the next iteration
    }

    // The property is an object
    if (Object.keys(o[k]).length === 0) {
      delete o[k]; // The object had no properties, so delete that property
    }
    return o;
  }
}

Handling nested objects using recursion

Now you want to make it recursive so that it will operate on nested objects. So we already have tested if o[k] is an object, and we've tested if there are properties, so if there are, we simply call the function again with that nested object.

function clearEmpties(o) {
  for (var k in o) {
    if (!o[k] || typeof o[k] !== "object") {
      continue // If null or not an object, skip to the next iteration
    }

    // The property is an object
    clearEmpties(o[k]); // <-- Make a recursive call on the nested object
    if (Object.keys(o[k]).length === 0) {
      delete o[k]; // The object had no properties, so delete that property
    }
  }
    return o;
}

So just as the original call to clearEmpties removes properties of the given object that reference an empty object, likewise the recursive call will do the same for the nested objects.


Live demo:

var object = {
  a: {
    b: 1,
    c: {
      a: 1,
      d: {},
      e: { // will need to be removed after f has been removed
         f: {} 
      }
    }
  },

  b: {}
};

clearEmpties(object);
console.log(object);

function clearEmpties(o) {
  for (var k in o) {
    if (!o[k] || typeof o[k] !== "object") {
      continue
    }

    clearEmpties(o[k]);
    if (Object.keys(o[k]).length === 0) {
      delete o[k];
    }
  }
  return o;
}


Short version using Underscore and functional style

function clearEmpties(o) {
  if (_.isFunction(o) || !_.isObject(o)) return o;
  return _.chain(o)
    .mapObject(clearEmpties)
    .pick(p => !(_.isObject(p) && _.isEmpty(p)))
    .value();
}

Short version using lodash and functional style - works with treeshaking

import { isFunction, isObject, isEmpty, isArray, isPlainObject, fromPairs } from "lodash-es";

const removeEmtpyObjects = (o) => {
    if (isFunction(o) || !isPlainObject(o)) return o;

    if (isArray(o)) return o.map(removeEmtpyObjects);

    return fromPairs(
        Object.entries(o)
            .map(([k, v]) => [k, removeEmtpyObjects(v)])
            .filter(([k, v]) => !(v == null || (isObject(v) && isEmpty(v))))
    );
};
Sign up to request clarification or add additional context in comments.

20 Comments

I think you should return o no?
@GeorgiK.: No need, since all you're wanting to do is mutate the existing object structure with delete. However, if you want it to return the original object, you certainly can have it do that.
I've tried it, the objects with {} still remain there
Finally found the term I was looking for: "arm's length recursion", and an opinionated view on it on quora.com.
Note that this will remove Date properties as well, which too are objects. Use something like Object.prototype.toString.call(o[k]) != '[object Object]'
|
2

I had this same problem and with the addition that my object may contain arrays with empty elements that need to be removed as well.

I ended up with this quick and dirty solution.

If you want to define what "empty" means to you, I also added a different function. In my case, I also needed empty strings.

  function isEmpty(obj) {
        if (obj === '' || obj === null || JSON.stringify(obj) === '{}' || JSON.stringify(obj) === '[]' || (obj) === undefined || (obj) === {}) {
            return true
        } else {
            return false
        }
    }
    function removeEmpty(o) {
        if (typeof o !== "object") {
            return o;
        }
        let oKeys = Object.keys(o)
        for (let j = 0; j < oKeys.length; j++) {
            let p = oKeys[j]
            switch (typeof (o[p])) {
                case 'object':
                    if (Array.isArray(o[p])) {
                        for (let i = 0; i < o[p].length; i++) {
                            o[p][i] = removeEmpty(o[p][i])
                            if (isEmpty(o[p][i])) {
                                o[p].splice(i, 1)
                                i--
                            }
                        }
                        if (o[p].length === 0) {
                            if (Array.isArray(o)) {
                                o.splice(parseInt(p), 1)
                                j--
                            } else {
                                delete o[p]
                            }
                        }
                    } else {
                        if (isEmpty(o[p])) {
                            delete o[p]
                        } else {
                            o[p] = removeEmpty(o[p])
                            if (isEmpty(o[p])) {
                                delete o[p]
                            }
                        }
                    }
                    break
                default:
                    if (isEmpty(o[p])) {
                        delete o[p]
                    }
                    break
            }
    
        }
        if (Object.keys(o).length === 0) {
            return
        }
        return o
    }

Input:

var a = {
b: 1,
c: {
    d: [1, [[], [], [[[1], []]]], [2, [[], [[]]]], [], [[]]]
},
e: {
    f: [{}, { g: 1 }]
},
h: {
    i: { j: { k: undefined, l: null, m: { n: "", o: 1 } } }
},
p: { q: { r: 1 } }
}
removeEmpty(a)

Output:

   {
    "b": 1,
    "c": {
        "d": [1, [[[[1]]]], [2]]
    },
    "e": {
        "f": [{"g": 1}]
    },
    "h": {
        "i": {
            "j": {
                "m": {
                    "o": 1
                }
            }
        }
    },
    "p": {
        "q": {
            "r": 1
        }
    }
}

Comments

0

This function returns the new cleared object. You can keep arrays on your wish... You can edit the first "if" to choose what to keep or remove.

function clearObjects(obj, keepArrays) {
    return Object.entries(obj)
        .reduce((acc, [key, value]) => {
            if (value == null ||
                (typeof value === 'string' && !value.trim()) ||
                ((typeof value === 'object' &&
                        Object.keys(value).length === 0) &&
                    !(keepArrays && Array.isArray(value))
                )
            ) {
                return acc;
            }

            if (Object.getPrototypeOf(value).isPrototypeOf(Object)) {
                const newObj = clearObjects(value, keepArrays);

                if (Object.keys(newObj).length > 0) {
                    acc[key] = newObj;
                }

                return acc;
            }

            acc[key] = value;
            return acc;
        }, {});
}

Comments

-2
function clean(obj) {
  for (var propName in obj) { 
    if (obj[propName] === null || obj[propName] === undefined) {
      delete obj[propName];      }
  }
}

EDIT:

function clean(obj) {
      for (var propName in obj) {
        if(typeof obj[propName]=="object")
          clean(obj[propName])
        if (obj[propName] === null || obj[propName] === undefined) 
          delete obj[propName];      
       }
    }

7 Comments

obj[propName] === undefined || obj[propName] === undefined ?
You can use if (obj[propName] == null) { to handle both cases at the same time. It will be true if the property is either null or undefined. However, your solution doesn't check what the OP is looking for and doesn't handle nested objects.
still doesnt clean my {}, they remain as they are. :(
now what should work? You have 2 completely different versions shown and no written explanation whatsoever
@charlietfl do you have a solution?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.