Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove empty & null values from nested object (ES6) - Clean nested Objects

I got an object which looks like this :

{
    "a": "string not empty",
    "b": {
        "c": "string not empty",       
    },
    "d": {
        "e": false,
        "f": 0,
        "g": true,
        "h": 10
    },
    "i": {
        "j": 0,
        "k": null
    },
    "l": {
        "m": null
    },
    "n": {
        "o": 1,
        "p": "string (not empty)",
        "q": {}
    },
    "r": [],
    "l": "2000-01-01T01:01:00.000Z",
}

Thanks to the code provided by here : https://stackoverflow.com/a/38364486/3912805 I can now remove all null values of my nested object.

I used this function so far to removeNull :

removeNull = (obj) => {
  Object.keys(obj).forEach(key =>
    (obj[key] && typeof obj[key] === 'object') && removeNull(obj[key]) ||
    (obj[key] === undefined || obj[key] === null) && delete obj[key]
  );
  return obj;
};

But I would like to enhance this function to allow me to remove all empty arrays or any empty collection which may exists in my nested object.

Final results should be without k, l & m, q, r, l:

{
    "a": "string not empty",
    "b": {
        "c": "string not empty",       
    },
    "d": {
        "e": false,
        "f": 0,
        "g": true,
        "h": 10
    },
    "i": {
        "j": 0
    },
    "n": {
        "o": 1,
        "p": "string (not empty)"
    },
    "l": "2000-01-01T01:01:00.000Z",
}

I need to keep all values which were set to 0 or to false.

I would like to enhance this removeNull's method using ES6 method, but so far I failed to do it.

I also tried old school method which was used for this How to deeply remove null values, empty objects and empty array from an object

itemToBool = item => {
  if (typeof item !== 'object' || item === null) return item;
  const cleanedItem = cleanObject(item);
  return Object.keys(cleanedItem).length !== 0 && cleanedItem;
};

cleanObject = obj => {
  if (Array.isArray(obj)) {
    const newArr = obj.map(itemToBool).filter(Boolean);
    return newArr.length && newArr;
  }
  const newObj = Object.entries(obj).reduce((a, [key, val]) => {
    const newVal = itemToBool(val);
    if (newVal !== null || newVal === false) a[key] = newVal;
    return a;
  }, {});
  return Object.keys(newObj).length > 0 && newObj;
};

but it fails too.

like image 637
Toucouleur Avatar asked Sep 17 '18 12:09

Toucouleur


3 Answers

You could take an straight forward approach by iterating the key/value pairs of the object and iterate nested iterable objects first and then delete the unwanted keys.

function clean(object) {
    Object
        .entries(object)
        .forEach(([k, v]) => {
            if (v && typeof v === 'object') {
                clean(v);
            }
            if (v && typeof v === 'object' && !Object.keys(v).length || v === null || v === undefined) {
                if (Array.isArray(object)) {
                    object.splice(k, 1);
                } else {
                    delete object[k];
                }
            }
        });
    return object;
}

var object = { a: "string not empty", b: { c: "string not empty" }, d: { e: false, f: 0, g: true, h: 10 }, i: { j: 0, k: null }, l: { m: null }, n: { o: 1, p: "string (not empty)", q: {} }, r: [{ foo: null }] };

console.log(clean(object));
.as-console-wrapper { max-height: 100% !important; top: 0; }
like image 180
Nina Scholz Avatar answered Nov 14 '22 06:11

Nina Scholz


You can exploit JSON.stringify and it's optional second argument replacer but be aware the following code removes null and undefined.

const sanitize = (obj) => {
  return JSON.parse(JSON.stringify(obj, (key, value) => {
    return (value === null ? undefined : value);
  }));
};

const obj = {
  "a": "string not empty",
  "b": {
    "c": "string not empty",
  },
  "d": {
    "e": false,
    "f": 0,
    "g": true,
    "h": 10
  },
  "i": {
    "j": 0,
    "k": null
  },
  "l": {
    "m": null
  },
  "n": {
    "o": 1,
    "p": "string (not empty)",
    "q": {}
  },
  "r": [],
  "l": "2000-01-01T01:01:00.000Z",
}

console.log(sanitize(obj))
like image 45
Moriarty Avatar answered Nov 14 '22 07:11

Moriarty


Thanks to Nina Scholz, my enhanced version will be :

cleanObject = function(object) {
    Object
        .entries(object)
        .forEach(([k, v]) => {
            if (v && typeof v === 'object')
                cleanObject(v);
            if (v && 
                typeof v === 'object' && 
                !Object.keys(v).length || 
                v === null || 
                v === undefined ||
                v.length === 0
            ) {
                if (Array.isArray(object))
                    object.splice(k, 1);
                else if (!(v instanceof Date))
                    delete object[k];
            }
        });
    return object;
}
like image 7
Toucouleur Avatar answered Nov 14 '22 07:11

Toucouleur