javascriptnode.jsunderscore.js

Remove empty objects from an object


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.


Solution

  • 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))))
        );
    };