Toucouleur
Toucouleur

Reputation: 1242

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.

Upvotes: 15

Views: 22187

Answers (11)

Ashok kumawat
Ashok kumawat

Reputation: 551

you can try this

var testvar = {
  test1: null,
  test2: 'string',
  test3: 3,
}

function removenull(obj) {
  for (var propName in obj) {
    if (obj[propName] === null || obj[propName] === undefined) {
      delete obj[propName];
    }
  }
  return obj
}

console.log(testvar);
console.log(removenull(testvar));

Upvotes: 0

Muhammad Rifat
Muhammad Rifat

Reputation: 1

// remove all falsy attributes for all layers of object

const removeFalsyProps = (body) => {
  let data = body;

  Object.keys(data).forEach((key) => {
    let value = data[key];

    // trim string type value
    if (typeof value == 'string') {
      value = value.trim();
    }

    // check value is falsy or not, then delete the property
    if (!value) {
      delete data[key];
    }

    // check value is object or not, If object then recursively call. (In JS null and array is object type)
    if (typeof value == 'object' && value != null && !Array.isArray(value)) {
      removeFalsyProps(value);
    }
  });

  return data;
};

const myOb = {
  a: 2,
  y: undefined,
  x: 0,
  s: null,
  b: '     ',
  c: { d: 8, i: '', j: { k: 1, l: '', m: { o: null, p: 'seven' } } },
  e: 'test',
  h: { a: '', b: 8, c: { d: 'de', e: 0 } },
  i: { array: ['hello'], bc: '' },
  j: 45,
  array: [],
};

console.log(removeFalsyProps(myOb));

Upvotes: 0

Kai
Kai

Reputation: 3663

have just solved the same issue, so I want to share with you guys. My code also clean nested object and array, can be customize depend on your requirement :


cleanObject = (input) => {
  if (typeof input === 'object' && input !== null) {
    if(Array.isArray(input)) {
      return input.map(cleanObject)
                  .filter(item => item !== null && item !== undefined) 
    }

    return Object.fromEntries(
      Object.entries(input)
            .map(([key, val]) => [key, cleanObject(val)])
            .filter(([k, v]) => v !== null && v !== undefined)
    );

  } 

  return input;
}
// testcase:
 const testVal = {
  a: 1,
  b: 2,
  c: undefined,
  d: { a: 99, b: null },
  e: { a: 1, b: 2, d: undefined, g: null, e: 0 },
  f: [1, 0, null, undefined],
  g: [1, 2],
  h: { aa: 1, bb: { c: 1, d: [111, null], e: 'hello' } },
};

cleanObject(testVal);

Upvotes: 1

Chidiebere Nwachukwu
Chidiebere Nwachukwu

Reputation: 416

function cleanObject(obj: object) {
  // remove all keys with undefined values in nested objects
  const cleaned = Object.entries(obj).reduce((acc, [key, val]) => {
    if (val && typeof val === 'object') {
      val = cleanObject(val);
    }
    if (val !== undefined) {
      acc[key] = val;
    }
    return acc;
  }, {});
  return cleaned;
}
const data = {
  a: 50,
  b: 90,
  c: undefined,
  d: undefined,
  e: {
    a: 90,
    b: 80,
    c: undefined,
    d: undefined,
  },
  f: {
    a: undefined,
    b: undefined,
    c: undefined,
    d: undefined,
  },
};

console.log(cleanObject(data));

output => { a: 50, b: 90, e: { a: 90, b: 80 }, f: {} }

Upvotes: 0

Sar
Sar

Reputation: 31

To remove empty arrays, strings, null, undefined values

function removeEmptyElements(obj) {
  if (Array.isArray(obj)) {
    obj.forEach((element, index) => obj.splice(index, 1, removeEmptyElements(element)));
    return obj;
  }
  return Object.fromEntries(Object.entries(obj)
    .filter(([, v]) => (Array.isArray(v) ? v.length !== 0 : (v !== null && v !== '' && v !== undefined)))
    .map(([k, v]) => [k, v === (Object(v)) ? removeEmptyElements(v) : v]));
}

Upvotes: 3

Kiwi Rupela
Kiwi Rupela

Reputation: 2348

I tried it by as follow

   const test = {
       a: '',
       b: 1,
       c: [],
       d: {
        e: null,
        f: 0,
        g: undefined,
        h: {
            i: 'test',
            j: {},
            k: '',
            l: {
                m: 'yo test',
                n: 'go for it'
            }
        }
     },
    e: 'yo tested'
    };

    const JS_PRIMITIVE_TYPES = { 'string': 1, 'number': 1, 'undefined': 1, 'boolean': 1, 
    'symbol': 1 }

    const isValuePrimitiveType = (value) => {
    const typeOfVal = typeof value;
    if (JS_PRIMITIVE_TYPES.hasOwnProperty(typeOfVal)) {
        return true;
    }
    return false;
    }

    /* MAIN Function which runs and call other functions
       allKeys : keys of object Object.keys(test);
       badJson : json which needs to be update
    */
      const iterateObjForRemoveEmptyElem = (badJson, allKeys) => {
       for (let index = 0; index < allKeys.length; index++) {
          const key = allKeys[index];
          if (isEmpty(badJson[key])) {
             delete badJson[key];
          } else if (Array.isArray(badJson[key]) || isValuePrimitiveType(badJson[key])) {
            continue;
         }
         else {
            const newKeys = Object.keys(badJson[key]);
            const newJson = Object.assign({}, badJson[key]);
            badJson[key] = iterateObjForRemoveEmptyElem(newJson, newKeys);
        }
    }
    return badJson;
    }



    const isEmpty = (val) => {
     if(val === '' || val === null || val === undefined ) {
         return true;
     } else if (Array.isArray(val) && val.length === 0){
        return true;
     } else if(typeof val === 'object' && Object.keys(val).length === 0){
        return true;
     }
    return false;
    }

    const myKeys = Object.keys(test);
    console.log("Final Result:::::",JSON.stringify(iterateObjForRemoveEmptyElem(test,myKeys)));

This works for me upto nth level

Upvotes: 0

Prateek Chitransh
Prateek Chitransh

Reputation: 171

//sample Json Response
var items = {
            name: 'test',
            randomArray: [],
             randomObject: {
                id: null,
                someObject: {},
                someInternalArray: [],
                someUndefinedObject: undefined,
            },
              New name: null,
               nestedObject: [
                {
                      emp: {
                        id: null,
                    },
                   empAssets: 2,
                   joiningDate: {
                        startDate: null,
                        endDate: '2019/12/01',
                         Addresses: [],
                    },
                },
            ],
        };
       this.removeEmptyKeys(items);
        console.log('the final items ‘,items);

//Removing logic 
removeEmptyKeys(yourObject) {
        Object.keys(yourObject).forEach(key => {
            if (
                Object.prototype.toString.call(yourObject[key]) === '[object Date]' &&
                (yourObject[key].toString().length === 0 ||
                    yourObject[key].toString() === 'Invalid Date')
            ) {
                delete yourObject[key];
            } else if (yourObject[key] && typeof yourObject[key] === 'object') {
                this.removeEmptyKeysFromObject(yourObject[key]);
            } else if (yourObject[key] == null || yourObject[key] === '') {
                delete yourObject[key];
            }

            if (
                yourObject[key] &&
                typeof yourObject[key] === 'object' &&
                Object.keys(yourObject[key]).length === 0 &&
                Object.prototype.toString.call(yourObject[key]) !== '[object Date]'
            ) {
                delete yourObject[key];
            }
        });
        return yourObject;
    }

Remove undefined, null, empty string , empty Arrays. Upvote if it helps.

Upvotes: -2

Moriarty
Moriarty

Reputation: 4137

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))

Upvotes: 14

Toucouleur
Toucouleur

Reputation: 1242

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

Upvotes: 9

Koushik Chatterjee
Koushik Chatterjee

Reputation: 4175

If you don't want to mutate the object and need a new copy, then you can stringify the object to json and parse it, and filter at the time of parsing. If you don't need the source object then you can override the result into same reference. Its may not the performance efficient approach but obviously much cleaner and not a self recursive approach.

var 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": [],
    "s": {"t": null},
    "u": [null, {"v": {}}]
}
function copyNonEmpty(o) {
  let ignores = [null, undefined, ""],
    isNonEmpty = d => !ignores.includes(d) && (typeof(d) !== "object" || Object.keys(d).length)
  return JSON.parse(JSON.stringify(o), function(k, v) {
    if (isNonEmpty(v))
      return v;
  });
}

var res = copyNonEmpty(obj);
console.log(JSON.stringify(res, null, 4));

If value is Object or Array then typeof will return object and Object.keys will return a array of keys for both the cases ("0", "1",2... in case of array), and the array length (of keys) will 0 if its an empty array or object. So, conditionally, it will must not (null, undefined or "") and (either a non object/array OR object/array which is non-empty and then you can take that value.

Upvotes: 3

Nina Scholz
Nina Scholz

Reputation: 386560

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

Upvotes: 15

Related Questions