53

Can you please tell me how to remove all null and empty string values from an object? I am getting an error while deleting the key.

This is what I have so far, but it doesn't work properly:

$.each(sjonObj, function(key, value) {
    if(value == "" || value == null) {
        delete sjonObj.key;
    }
});

var sjonObj= {
  "executionMode": "SEQUENTIAL",
  "coreTEEVersion": "3.3.1.4_RC8",
  "testSuiteId": "yyy",
  "testSuiteFormatVersion": "1.0.0.0",
  "testStatus": "IDLE",
  "reportPath": "",
  "startTime": 0,
  "durationBetweenTestCases": 20,
  "endTime": 0,
  "lastExecutedTestCaseId": 0,
  "repeatCount": 0,
  "retryCount": 0,
  "fixedTimeSyncSupported": false,
  "totalRepeatCount": 0,
  "totalRetryCount": 0,
  "summaryReportRequired": "true",
  "postConditionExecution": "ON_SUCCESS",
  "testCaseList": [
    {
      "executionMode": "SEQUENTIAL",
      "commandList": [
        
      ],
      "testCaseList": [
        
      ],
      "testStatus": "IDLE",
      "boundTimeDurationForExecution": 0,
      "startTime": 0,
      "endTime": 0,
      "label": null,
      "repeatCount": 0,
      "retryCount": 0,
      "totalRepeatCount": 0,
      "totalRetryCount": 0,
      "testCaseId": "a",
      "summaryReportRequired": "false",
      "postConditionExecution": "ON_SUCCESS"
    },
    {
      "executionMode": "SEQUENTIAL",
      "commandList": [
        
      ],
      "testCaseList": [
        {
          "executionMode": "SEQUENTIAL",
          "commandList": [
            {
              "commandParameters": {
                "serverAddress": "www.ggp.com",
                "echoRequestCount": "",
                "sendPacketSize": "",
                "interval": "",
                "ttl": "",
                "addFullDataInReport": "True",
                "maxRTT": "",
                "failOnTargetHostUnreachable": "True",
                "failOnTargetHostUnreachableCount": "",
                "initialDelay": "",
                "commandTimeout": "",
                "testDuration": ""
              },
              "commandName": "Ping",
              "testStatus": "IDLE",
              "label": "",
              "reportFileName": "tc_2-tc_1-cmd_1_Ping",
              "endTime": 0,
              "startTime": 0,
              "repeatCount": 0,
              "retryCount": 0,
              "totalRepeatCount": 0,
              "totalRetryCount": 0,
              "postConditionExecution": "ON_SUCCESS",
              "detailReportRequired": "true",
              "summaryReportRequired": "true"
            }
          ],
          "testCaseList": [
            
          ],
          "testStatus": "IDLE",
          "boundTimeDurationForExecution": 0,
          "startTime": 0,
          "endTime": 0,
          "label": null,
          "repeatCount": 0,
          "retryCount": 0,
          "totalRepeatCount": 0,
          "totalRetryCount": 0,
          "testCaseId": "dd",
          "summaryReportRequired": "false",
          "postConditionExecution": "ON_SUCCESS"
        }
      ],
      "testStatus": "IDLE",
      "boundTimeDurationForExecution": 0,
      "startTime": 0,
      "endTime": 0,
      "label": null,
      "repeatCount": 0,
      "retryCount": 0,
      "totalRepeatCount": 0,
      "totalRetryCount": 0,
      "testCaseId": "b",
      "summaryReportRequired": "false",
      "postConditionExecution": "ON_SUCCESS"
    }
  ]
};

$.each(sjonObj, function(key, value) {
    if(value == "" || value == null) {
        delete sjonObj.key;
    }
});

console.log(sjonObj);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

1
  • I used omit-empty and it works fine for '' & null. Commented Jul 3, 2017 at 7:49

13 Answers 13

72

You're deleting sjonObj.key, literally. You need to use array access notation:

delete sjonObj[key];

However, that will also delete where value is equal to 0, since you're not using strict comparison. Use === instead:

$.each(sjonObj, function(key, value){
    if (value === "" || value === null){
        delete sjonObj[key];
    }
});

However, this will only walk the object shallowly. To do it deeply, you can use recursion:

(function filter(obj) {
    $.each(obj, function(key, value){
        if (value === "" || value === null){
            delete obj[key];
        } else if (Object.prototype.toString.call(value) === '[object Object]') {
            filter(value);
        } else if ($.isArray(value)) {
            $.each(value, function (k,v) { filter(v); });
        }
    });
})(sjonObj);

var sjonObj = {
  "executionMode": "SEQUENTIAL",
  "coreTEEVersion": "3.3.1.4_RC8",
  "testSuiteId": "yyy",
  "testSuiteFormatVersion": "1.0.0.0",
  "testStatus": "IDLE",
  "reportPath": "",
  "startTime": 0,
  "durationBetweenTestCases": 20,
  "endTime": 0,
  "lastExecutedTestCaseId": 0,
  "repeatCount": 0,
  "retryCount": 0,
  "fixedTimeSyncSupported": false,
  "totalRepeatCount": 0,
  "totalRetryCount": 0,
  "summaryReportRequired": "true",
  "postConditionExecution": "ON_SUCCESS",
  "testCaseList": [
    {
      "executionMode": "SEQUENTIAL",
      "commandList": [
        
      ],
      "testCaseList": [
        
      ],
      "testStatus": "IDLE",
      "boundTimeDurationForExecution": 0,
      "startTime": 0,
      "endTime": 0,
      "label": null,
      "repeatCount": 0,
      "retryCount": 0,
      "totalRepeatCount": 0,
      "totalRetryCount": 0,
      "testCaseId": "a",
      "summaryReportRequired": "false",
      "postConditionExecution": "ON_SUCCESS"
    },
    {
      "executionMode": "SEQUENTIAL",
      "commandList": [
        
      ],
      "testCaseList": [
        {
          "executionMode": "SEQUENTIAL",
          "commandList": [
            {
              "commandParameters": {
                "serverAddress": "www.ggp.com",
                "echoRequestCount": "",
                "sendPacketSize": "",
                "interval": "",
                "ttl": "",
                "addFullDataInReport": "True",
                "maxRTT": "",
                "failOnTargetHostUnreachable": "True",
                "failOnTargetHostUnreachableCount": "",
                "initialDelay": "",
                "commandTimeout": "",
                "testDuration": ""
              },
              "commandName": "Ping",
              "testStatus": "IDLE",
              "label": "",
              "reportFileName": "tc_2-tc_1-cmd_1_Ping",
              "endTime": 0,
              "startTime": 0,
              "repeatCount": 0,
              "retryCount": 0,
              "totalRepeatCount": 0,
              "totalRetryCount": 0,
              "postConditionExecution": "ON_SUCCESS",
              "detailReportRequired": "true",
              "summaryReportRequired": "true"
            }
          ],
          "testCaseList": [
            
          ],
          "testStatus": "IDLE",
          "boundTimeDurationForExecution": 0,
          "startTime": 0,
          "endTime": 0,
          "label": null,
          "repeatCount": 0,
          "retryCount": 0,
          "totalRepeatCount": 0,
          "totalRetryCount": 0,
          "testCaseId": "dd",
          "summaryReportRequired": "false",
          "postConditionExecution": "ON_SUCCESS"
        }
      ],
      "testStatus": "IDLE",
      "boundTimeDurationForExecution": 0,
      "startTime": 0,
      "endTime": 0,
      "label": null,
      "repeatCount": 0,
      "retryCount": 0,
      "totalRepeatCount": 0,
      "totalRetryCount": 0,
      "testCaseId": "b",
      "summaryReportRequired": "false",
      "postConditionExecution": "ON_SUCCESS"
    }
  ]
};

(function filter(obj) {
    $.each(obj, function(key, value){
        if (value === "" || value === null){
            delete obj[key];
        } else if (Object.prototype.toString.call(value) === '[object Object]') {
            filter(value);
        } else if (Array.isArray(value)) {
            value.forEach(function (el) { filter(el); });
        }
    });
})(sjonObj);

console.log(sjonObj)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


Note that if you're willing to use a library like lodash/underscore.js, you can use _.pick instead. However, you will still need to use recursion to filter deeply, since neither library provides a deep filter function.

sjonObj = (function filter(obj) {
    var filtered = _.pick(obj, function (v) { return v !== '' && v !== null; });
    return _.cloneDeep(filtered, function (v) { return v !== filtered && _.isPlainObject(v) ? filter(v) : undefined; });
})(sjonObj);

This variant has the added advantage of leaving the original object unmodified, but it does create an entirely new copy, which would be less efficient if you don't need the original object.

var sjonObj = {
  "executionMode": "SEQUENTIAL",
  "coreTEEVersion": "3.3.1.4_RC8",
  "testSuiteId": "yyy",
  "testSuiteFormatVersion": "1.0.0.0",
  "testStatus": "IDLE",
  "reportPath": "",
  "startTime": 0,
  "durationBetweenTestCases": 20,
  "endTime": 0,
  "lastExecutedTestCaseId": 0,
  "repeatCount": 0,
  "retryCount": 0,
  "fixedTimeSyncSupported": false,
  "totalRepeatCount": 0,
  "totalRetryCount": 0,
  "summaryReportRequired": "true",
  "postConditionExecution": "ON_SUCCESS",
  "testCaseList": [
    {
      "executionMode": "SEQUENTIAL",
      "commandList": [
        
      ],
      "testCaseList": [
        
      ],
      "testStatus": "IDLE",
      "boundTimeDurationForExecution": 0,
      "startTime": 0,
      "endTime": 0,
      "label": null,
      "repeatCount": 0,
      "retryCount": 0,
      "totalRepeatCount": 0,
      "totalRetryCount": 0,
      "testCaseId": "a",
      "summaryReportRequired": "false",
      "postConditionExecution": "ON_SUCCESS"
    },
    {
      "executionMode": "SEQUENTIAL",
      "commandList": [
        
      ],
      "testCaseList": [
        {
          "executionMode": "SEQUENTIAL",
          "commandList": [
            {
              "commandParameters": {
                "serverAddress": "www.ggp.com",
                "echoRequestCount": "",
                "sendPacketSize": "",
                "interval": "",
                "ttl": "",
                "addFullDataInReport": "True",
                "maxRTT": "",
                "failOnTargetHostUnreachable": "True",
                "failOnTargetHostUnreachableCount": "",
                "initialDelay": "",
                "commandTimeout": "",
                "testDuration": ""
              },
              "commandName": "Ping",
              "testStatus": "IDLE",
              "label": "",
              "reportFileName": "tc_2-tc_1-cmd_1_Ping",
              "endTime": 0,
              "startTime": 0,
              "repeatCount": 0,
              "retryCount": 0,
              "totalRepeatCount": 0,
              "totalRetryCount": 0,
              "postConditionExecution": "ON_SUCCESS",
              "detailReportRequired": "true",
              "summaryReportRequired": "true"
            }
          ],
          "testCaseList": [
            
          ],
          "testStatus": "IDLE",
          "boundTimeDurationForExecution": 0,
          "startTime": 0,
          "endTime": 0,
          "label": null,
          "repeatCount": 0,
          "retryCount": 0,
          "totalRepeatCount": 0,
          "totalRetryCount": 0,
          "testCaseId": "dd",
          "summaryReportRequired": "false",
          "postConditionExecution": "ON_SUCCESS"
        }
      ],
      "testStatus": "IDLE",
      "boundTimeDurationForExecution": 0,
      "startTime": 0,
      "endTime": 0,
      "label": null,
      "repeatCount": 0,
      "retryCount": 0,
      "totalRepeatCount": 0,
      "totalRetryCount": 0,
      "testCaseId": "b",
      "summaryReportRequired": "false",
      "postConditionExecution": "ON_SUCCESS"
    }
  ]
};

sjonObj = (function filter(obj) {
    var filtered = _.pick(obj, function (v) { return v !== '' && v !== null; });
    return _.cloneDeep(filtered, function (v) { return v !== filtered && _.isPlainObject(v) ? filter(v) : undefined; });
})(sjonObj);

console.log(sjonObj);
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>

Sign up to request clarification or add additional context in comments.

3 Comments

I'm not sure if it is mandated that '[object Object]' is returned. (Or maybe it is and I need to review the specification.)
@user2864740 It is if it's an object literal with no toString property, yes.
In Lodash 4.17.10 "pick" needs to be changed to "pickBy" and "cloneDeep" needs to be changed to "cloneDeepWith"
67

Using some ES6 / ES2015:

If you don't like to create an extra function and remove the items 'inline'.

Object.keys(obj).forEach(k => (!obj[k] && obj[k] !== undefined) && delete obj[k]);

jsbin

Same, written as a function.

const removeEmpty = (obj) => {
  Object.keys(obj).forEach((k) => (!obj[k] && obj[k] !== undefined) && delete obj[k]);
  return obj;
};

jsbin

This function uses recursion to delete items from nested objects as well:

const removeEmpty = (obj) => {
  Object.keys(obj).forEach(k =>
    (obj[k] && typeof obj[k] === 'object') && removeEmpty(obj[k]) ||
    (!obj[k] && obj[k] !== undefined) && delete obj[k]
  );
  return obj;
};

jsbin

Same as function before but with ES7 / 2016 Object.entries:

const removeEmpty = (obj) => {
  Object.entries(obj).forEach(([key, val])  =>
    (val && typeof val === 'object') && removeEmpty(val) ||
    (val === null || val === "") && delete obj[key]
  );
  return obj;
};

Same as third example but in plain ES5:

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

jsbin

6 Comments

Problem with this approach function removeEmpty(obj) { Object.keys(obj).forEach(function(key) { (obj[key] && typeof obj[key] === 'object') && removeEmpty(obj[key]) || (obj[key] === '' || obj[key] === null) && delete obj[key] }); return obj; }; It also removes where there obj[key] references to a boolean value (eg. false)
It should only remove items that === "" or === null. Make sure you use === instead of ==.
Sorry for asking, but I tried to add to your solution @Rotareti the way to check if either my obj[key] was empty but I failed to do it so far. May I ask you if you could add it to your original proposal ?
Please don't copy answers to multiple questions; this is the same as your answer to a similar question
For the record: I felt the Question was a duplicate and not the Answer. I appreciate duplicate Answers when we have not marked a Question as duplicate.
|
12

var data = [
   { "name": "bill", "age": 20 },
   { "name": "jhon", "age": 19 },
   { "name": "steve", "age": 16 },
   { "name": "larry", "age": 22 },
   null, null, null
];

//eliminate all the null values from the data
data = data.filter(function(x) { return x !== null }); 

console.log("data: " + JSON.stringify(data));

3 Comments

Simple! Straight! Perfect! Awesome! +1
This but on asociative objects?
@JorhelReyes can you please elobrate.
10

I would make use of the JSON parse reviver parameter to remove empty or null from nested objects.

const test = {
   a: 0,
   b: null,
   c: '',
   d: {
    e: 1,
    f: null
   },
   g: {
    h: null
   }
 }

const check = JSON.parse(JSON.stringify(test),
(key, value) => value === null || value === '' ? undefined : value);

console.log(check)

1 Comment

Good one, Learnt something new with reviver..
4

Building upon suryaPavan's answer this slight modification can cleanup the empty object after removing the invidival emptys inside the object or array. this ensures that you don't have an empty array or object hanging around.

function removeNullsInObject(obj) {
            if( typeof obj === 'string' || obj === "" ){
                return;
            }
            $.each(obj, function(key, value){
                if (value === "" || value === null){
                    delete obj[key];
                } else if ($.isArray(value)) {
                    if( value.length === 0 ){
                        delete obj[key];
                        return;
                    }
                    $.each(value, function (k,v) {
                        removeNullsInObject(v);
                    });
                    if( value.length === 0 ){
                        delete obj[key];
                    }
                } else if (typeof value === 'object') {
                    if( Object.keys(value).length === 0 ){
                        delete obj[key];
                        return;
                    }
                    removeNullsInObject(value);
                    if( Object.keys(value).length === 0 ){
                        delete obj[key];
                    }
                }
            });
 }

Comments

4

const myObject = {
  key1: "Hello",
  key2: null,
  key3: "",
  key4: undefined,
  key5: "World",
  key6: false,
  key7: true
};

const filteredObj = (obj) =>
  Object.entries(obj)
    .filter(([_, value]) => !!value || typeof value === "boolean")
    .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
      
      
      
console.log(filteredObj(myObject));

2 Comments

Not entirely correct: It removes false boolean values
@brunags ah good catch - edited snippet
3

Here is the optimized code snippet to remove empty arrays/objects as well:

function removeNullsInObject(obj) {
    if( typeof obj === 'string' ){ return; }
    $.each(obj, function(key, value){
        if (value === "" || value === null){
            delete obj[key];
        } else if ($.isArray(value)) {
            if( value.length === 0 ){ delete obj[key]; return; }
            $.each(value, function (k,v) {
                removeNullsInObject(v);
            });
        } else if (typeof value === 'object') {
            if( Object.keys(value).length === 0 ){ 
                delete obj[key]; return; 
            }
            removeNullsInObject(value);
        }
    }); 
 }

Thanks @Alexis king :)

Comments

3

According to Alexis King answer, here is a plain JavaScript version.

function cleanUp(obj) {
    for (var attrKey in obj) {
        var attrValue = obj[attrKey];
        if (attrValue === null || attrValue === "") {
            delete obj[attrKey];
        } else if (Object.prototype.toString.call(attrValue) === "[object Object]") {
            cleanUp(attrValue);
        } else if (Array.isArray(attrValue)) {
            attrValue.forEach(function (arrayValue) {
                cleanUp(arrayValue);
            });
        }
    }
}

Comments

2

You need to use the bracket notation because key is a variable holding the key as a value

$.each(sjonObj, function(key,value){
   // console.log(value);
    if(value==""||value==null){
        delete sjonObj[key];
    }

});

delete sjonObj.key deletes the property called key from sjonObj, instead you need to use key as a variable holding the property name.

Note: Still it will not handle the nested objects

Comments

2
function removeAllBlankOrNull(JsonObj) {
    $.each(JsonObj, function(key, value) {
        if (value === "" || value === null) {
            delete JsonObj[key];
        } else if (typeof(value) === "object") {
            JsonObj[key] = removeAllBlankOrNull(value);
        }
    });
    return JsonObj;
}

Deletes all empty strings and null values recursively. Fiddle

Comments

2

Enhancement to Alexis King's code to run without Jquery and removal of empty arrays and array of empty objects (With no properties) recursively.

var sjonObj = {
"executionMode": "SEQUENTIAL",
"coreTEEVersion": "3.3.1.4_RC8",
"testSuiteId": "yyy",
"testSuiteFormatVersion": "1.0.0.0",
"testStatus": "IDLE",
"reportPath": "",
"startTime": 0,
"durationBetweenTestCases": 20,
"endTime": 0,
"lastExecutedTestCaseId": 0,
"repeatCount": 0,
"retryCount": 0,
"fixedTimeSyncSupported": false,
"totalRepeatCount": 0,
"totalRetryCount": 0,
"summaryReportRequired": "true",
"postConditionExecution": "ON_SUCCESS",
"testCaseList": [{
        "executionMode": "SEQUENTIAL",
        "commandList": [{
            "sample1": "",
            "sample2": ""
        }],
        "testCaseList": [

        ],
        "testStatus": "IDLE",
        "boundTimeDurationForExecution": 0,
        "startTime": 0,
        "endTime": 0,
        "label": null,
        "repeatCount": 0,
        "retryCount": 0,
        "totalRepeatCount": 0,
        "totalRetryCount": 0,
        "testCaseId": "a",
        "summaryReportRequired": "false",
        "postConditionExecution": "ON_SUCCESS"
    },
    {
        "executionMode": "SEQUENTIAL",
        "commandList": [

        ],
        "testCaseList": [{
            "executionMode": "SEQUENTIAL",
            "commandList": [{
                "commandParameters": {
                    "serverAddress": "www.ggp.com",
                    "echoRequestCount": "",
                    "sendPacketSize": "",
                    "interval": "",
                    "ttl": "",
                    "addFullDataInReport": "True",
                    "maxRTT": "",
                    "failOnTargetHostUnreachable": "True",
                    "failOnTargetHostUnreachableCount": "",
                    "initialDelay": "",
                    "commandTimeout": "",
                    "testDuration": ""
                },
                "commandName": "Ping",
                "testStatus": "IDLE",
                "label": "",
                "reportFileName": "tc_2-tc_1-cmd_1_Ping",
                "endTime": 0,
                "startTime": 0,
                "repeatCount": 0,
                "retryCount": 0,
                "totalRepeatCount": 0,
                "totalRetryCount": 0,
                "postConditionExecution": "ON_SUCCESS",
                "detailReportRequired": "true",
                "summaryReportRequired": "true"
            }],
            "testCaseList": [

            ],
            "testStatus": "IDLE",
            "boundTimeDurationForExecution": 0,
            "startTime": 0,
            "endTime": 0,
            "label": null,
            "repeatCount": 0,
            "retryCount": 0,
            "totalRepeatCount": 0,
            "totalRetryCount": 0,
            "testCaseId": "dd",
            "summaryReportRequired": "false",
            "postConditionExecution": "ON_SUCCESS"
        }],
        "testStatus": "IDLE",
        "boundTimeDurationForExecution": 0,
        "startTime": 0,
        "endTime": 0,
        "label": null,
        "repeatCount": 0,
        "retryCount": 0,
        "totalRepeatCount": 0,
        "totalRetryCount": 0,
        "testCaseId": "b",
        "summaryReportRequired": "false",
        "postConditionExecution": "ON_SUCCESS"
    }
]};
function filter(obj) {
  for(let key in obj){
    if (obj[key] === "" || obj[key] === null){
        delete obj[key];
    } else if (Object.prototype.toString.call(obj[key]) === '[object Object]') {
            filter(obj[key]);
    } else if (Array.isArray(obj[key])) {
        if(obj[key].length == 0){
            delete obj[key];
        }else{
            for(let _key in obj[key]){
                filter(obj[key][_key]);
            }
            obj[key] = obj[key].filter(value => Object.keys(value).length !== 0);
            if(obj[key].length == 0){
                delete obj[key];
            }
        }
    }   
}};

filter(sjonObj);
console.log(JSON.stringify(sjonObj, null, 3));

Comments

0

Note: this doen't sanitize arrays:

import { isPlainObject } from 'lodash';

export const sanitize = (obj: {}) => {
  if (isPlainObject(obj)) {
    const sanitizedObj = {};

    for (const key in obj) {
      if (obj[key]) {
        sanitizedObj[key] = sanitize(obj[key]);
      }
    }

    return sanitizedObj;
  } else {
    return obj;
  }
};

Test:

  describe('sanitize', () => {
    it('should keep an object if there are no empty fields', () => {
      expect(sanitize({})).toEqual({});
      expect(sanitize({ foo: 'bar' })).toEqual({ foo: 'bar' });
      expect(sanitize({ content: { foo: 'bar' } })).toEqual({
        content: { foo: 'bar' },
      });
    });

    it('should remove empty fields from top level', () => {
      expect(sanitize({ foo: '', bar: 'baz' })).toEqual({ bar: 'baz' });
      expect(sanitize({ foo: null, bar: 'baz' })).toEqual({ bar: 'baz' });
      expect(sanitize({ foo: undefined, bar: 'baz' })).toEqual({ bar: 'baz' });
    });

    it('should remove nested empty fields', () => {
      expect(sanitize({ content: { foo: '', bar: 'baz' } })).toEqual({
        content: { bar: 'baz' },
      });
      expect(sanitize({ content: { foo: null, bar: 'baz' } })).toEqual({
        content: { bar: 'baz' },
      });
      expect(sanitize({ content: { foo: undefined, bar: 'baz' } })).toEqual({
        content: { bar: 'baz' },
      });
    });
  });

Comments

-2

There is a very simple way to remove NULL values from JSON object. By default JSON object includes NULL values. Following can be used to remove NULL from JSON string

JsonConvert.SerializeObject(yourClassObject, new JsonSerializerSettings() {
                                       NullValueHandling = NullValueHandling.Ignore})) 

1 Comment

For clarity, this is C# / ASP.NET (which the original question did not reference at all).