How do I remove all attributes which are undefined or null in a JavaScript object?
(Question is similar to this one for Arrays)
ES10/ES2019 examples
A simple one-liner (returning a new object).
let o = Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
Same as above but written as a function.
function removeEmpty(obj) {
return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
}
This function uses recursion to remove items from nested objects.
function removeEmpty(obj) {
return Object.fromEntries(
Object.entries(obj)
.filter(([_, v]) => v != null)
.map(([k, v]) => [k, v === Object(v) ? removeEmpty(v) : v])
);
}
ES6/ES2015 examples
A simple one-liner. Warning: This mutates the given object instead of returning a new one.
Object.keys(obj).forEach((k) => obj[k] == null && delete obj[k]);
A single declaration (not mutating the given object).
let o = Object.keys(obj)
.filter((k) => obj[k] != null)
.reduce((a, k) => ({ ...a, [k]: obj[k] }), {});
Same as above but written as a function.
function removeEmpty(obj) {
return Object.entries(obj)
.filter(([_, v]) => v != null)
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
}
This function uses recursion to remove items from nested objects.
function removeEmpty(obj) {
return Object.entries(obj)
.filter(([_, v]) => v != null)
.reduce(
(acc, [k, v]) => ({ ...acc, [k]: v === Object(v) ? removeEmpty(v) : v }),
{}
);
}
Same as the function above, but written in an imperative (non-functional) style.
function removeEmpty(obj) {
const newObj = {};
Object.entries(obj).forEach(([k, v]) => {
if (v === Object(v)) {
newObj[k] = removeEmpty(v);
} else if (v != null) {
newObj[k] = obj[k];
}
});
return newObj;
}
ES5/ES2009 examples
In the old days things were a lot more verbose.
This is a non recursive version written in a functional style.
function removeEmpty(obj) {
return Object.keys(obj)
.filter(function (k) {
return obj[k] != null;
})
.reduce(function (acc, k) {
acc[k] = obj[k];
return acc;
}, {});
}
This is a non recursive version written in an imperative style.
function removeEmpty(obj) {
const newObj = {};
Object.keys(obj).forEach(function (k) {
if (obj[k] && typeof obj[k] === "object") {
newObj[k] = removeEmpty(obj[k]);
} else if (obj[k] != null) {
newObj[k] = obj[k];
}
});
return newObj;
}
And a recursive version written in a functional style.
function removeEmpty(obj) {
return Object.keys(obj)
.filter(function (k) {
return obj[k] != null;
})
.reduce(function (acc, k) {
acc[k] = typeof obj[k] === "object" ? removeEmpty(obj[k]) : obj[k];
return acc;
}, {});
}
You can loop through the object:
var test = {
test1: null,
test2: 'somestring',
test3: 3,
}
function clean(obj) {
for (var propName in obj) {
if (obj[propName] === null || obj[propName] === undefined) {
delete obj[propName];
}
}
return obj
}
console.log(test);
console.log(clean(test));
If you're concerned about this property removal not running up object's proptype chain, you can also:
function clean(obj) {
var propNames = Object.getOwnPropertyNames(obj);
for (var i = 0; i < propNames.length; i++) {
var propName = propNames[i];
if (obj[propName] === null || obj[propName] === undefined) {
delete obj[propName];
}
}
}
A few notes on null vs undefined:
test.test1 === null; // true
test.test1 == null; // true
test.notaprop === null; // false
test.notaprop == null; // true
test.notaprop === undefined; // true
test.notaprop == undefined; // true
Shortest one liners for ES6+
Filter all falsy values ( "", 0, false, null, undefined )
Object.entries(obj).reduce((a,[k,v]) => (v ? (a[k]=v, a) : a), {})
Filter null and undefined values:
Object.entries(obj).reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {})
Filter ONLY null
Object.entries(obj).reduce((a,[k,v]) => (v === null ? a : (a[k]=v, a)), {})
Filter ONLY undefined
Object.entries(obj).reduce((a,[k,v]) => (v === undefined ? a : (a[k]=v, a)), {})
Recursive Solutions: Filters null and undefined
For Objects:
const cleanEmpty = obj => Object.entries(obj)
.map(([k,v])=>[k,v && typeof v === "object" ? cleanEmpty(v) : v])
.reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {});
For Objects and Arrays:
const cleanEmpty = obj => {
if (Array.isArray(obj)) {
return obj
.map(v => (v && typeof v === 'object') ? cleanEmpty(v) : v)
.filter(v => !(v == null));
} else {
return Object.entries(obj)
.map(([k, v]) => [k, v && typeof v === 'object' ? cleanEmpty(v) : v])
.reduce((a, [k, v]) => (v == null ? a : (a[k]=v, a)), {});
}
}
If you are using lodash or underscore.js, here is a simple solution:
var obj = {name: 'John', age: null};
var compacted = _.pickBy(obj);
This will only work with lodash 4, pre lodash 4 or underscore.js, use _.pick(obj, _.identity);
If somebody needs a recursive version of Owen's (and Eric's) answer, here it is:
/**
* Delete all null (or undefined) properties from an object.
* Set 'recurse' to true if you also want to delete properties in nested objects.
*/
function delete_null_properties(test, recurse) {
for (var i in test) {
if (test[i] === null) {
delete test[i];
} else if (recurse && typeof test[i] === 'object') {
delete_null_properties(test[i], recurse);
}
}
}
JSON.stringify removes the undefined keys.
removeUndefined = function(json){
return JSON.parse(JSON.stringify(json))
}
You can use a combination of JSON.stringify, its replacer parameter, and JSON.parse to turn it back into an object. Using this method also means the replacement is done to all nested keys within nested objects.
Example Object
var exampleObject = {
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3],
object: {
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3]
},
arrayOfObjects: [
{
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3]
},
{
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3]
}
]
};
Replacer Function
function replaceUndefinedOrNull(key, value) {
if (value === null || value === undefined) {
return undefined;
}
return value;
}
Clean the Object
exampleObject = JSON.stringify(exampleObject, replaceUndefinedOrNull);
exampleObject = JSON.parse(exampleObject);
CodePen example
Simplest possible Lodash solution to return an object with the null and undefined values filtered out.
_.omitBy(obj, _.isNil)
You can do a recursive removal in one line using json.stringify's replacer argument
const removeEmptyValues = obj => (
JSON.parse(JSON.stringify(obj, (k,v) => v ?? undefined))
)
Usage:
removeEmptyValues({a:{x:1,y:null,z:undefined}}) // Returns {a:{x:1}}
As mentioned in Emmanuel's comment, this technique only worked if your data structure contains only data types that can be put into JSON format (strings, numbers, lists, etc).
(This answer has been updated to use the new Nullish Coalescing operator. depending on browser support needs you may want to use this function instead: (k,v) => v!=null ? v : undefined)
You are probably looking for the delete keyword.
var obj = { };
obj.theProperty = 1;
delete obj.theProperty;
you can do shorter with ! condition
var r = {a: null, b: undefined, c:1};
for(var k in r)
if(!r[k]) delete r[k];
Remember in usage : as #semicolor announce in comments: This would also delete properties if the value is an empty string, false or zero
Remove all the properties with null and undefined
let obj = {
"id": 1,
"firstName": null,
"lastName": null,
"address": undefined,
"role": "customer",
"photo": "fb79fd5d-06c9-4097-8fdc-6cebf73fab26/fc8efe82-2af4-4c81-bde7-8d2f9dd7994a.jpg",
"location": null,
"idNumber": null,
};
let result = Object.entries(obj).reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {});
console.log(result)
I have same scenario in my project and achieved using following method.
It works with all data types, few mentioned above doesn't work with date and empty arrays .
removeEmptyKeysFromObject.js
removeEmptyKeysFromObject(obj) {
Object.keys(obj).forEach(key => {
if (Object.prototype.toString.call(obj[key]) === '[object Date]' && (obj[key].toString().length === 0 || obj[key].toString() === 'Invalid Date')) {
delete obj[key];
} else if (obj[key] && typeof obj[key] === 'object') {
this.removeEmptyKeysFromObject(obj[key]);
} else if (obj[key] == null || obj[key] === '') {
delete obj[key];
}
if (obj[key]
&& typeof obj[key] === 'object'
&& Object.keys(obj[key]).length === 0
&& Object.prototype.toString.call(obj[key]) !== '[object Date]') {
delete obj[key];
}
});
return obj;
}
pass any object to this function removeEmptyKeysFromObject()
Using ramda#pickBy you will remove all null, undefined and false values:
const obj = {a:1, b: undefined, c: null, d: 1}
R.pickBy(R.identity, obj)
As #manroe pointed out, to keep false values use isNil():
const obj = {a:1, b: undefined, c: null, d: 1, e: false}
R.pickBy(v => !R.isNil(v), obj)
Shorter ES6 pure solution, convert it to an array, use the filter function and convert it back to an object.
Would also be easy to make a function...
Btw. with this .length > 0 i check if there is an empty string / array, so it will remove empty keys.
const MY_OBJECT = { f: 'te', a: [] }
Object.keys(MY_OBJECT)
.filter(f => !!MY_OBJECT[f] && MY_OBJECT[f].length > 0)
.reduce((r, i) => { r[i] = MY_OBJECT[i]; return r; }, {});
JS BIN https://jsbin.com/kugoyinora/edit?js,console
Functional and immutable approach, without .filter and without creating more objects than needed
Object.keys(obj).reduce((acc, key) => (obj[key] === undefined ? acc : {...acc, [key]: obj[key]}), {})
Instead of delete the property, you can also create a new object with the keys that are not null.
const removeEmpty = (obj) => {
return Object.keys(obj).filter(key => obj[key]).reduce(
(newObj, key) => {
newObj[key] = obj[key]
return newObj
}, {}
)
}
For a deep search I used the following code, maybe it will be useful for anyone looking at this question (it is not usable for cyclic dependencies ) :
function removeEmptyValues(obj) {
for (var propName in obj) {
if (!obj[propName] || obj[propName].length === 0) {
delete obj[propName];
} else if (typeof obj[propName] === 'object') {
removeEmptyValues(obj[propName]);
}
}
return obj;
}
Here is a comprehensive recursive function (originally based on the one by #chickens) that will:
recursively remove what you tell it to defaults=[undefined, null, '', NaN]
Correctly handle regular objects, arrays and Date objects
const cleanEmpty = function(obj, defaults = [undefined, null, NaN, '']) {
if (!defaults.length) return obj
if (defaults.includes(obj)) return
if (Array.isArray(obj))
return obj
.map(v => v && typeof v === 'object' ? cleanEmpty(v, defaults) : v)
.filter(v => !defaults.includes(v))
return Object.entries(obj).length
? Object.entries(obj)
.map(([k, v]) => ([k, v && typeof v === 'object' ? cleanEmpty(v, defaults) : v]))
.reduce((a, [k, v]) => (defaults.includes(v) ? a : { ...a, [k]: v}), {})
: obj
}
USAGE:
// based off the recursive cleanEmpty function by #chickens.
// This one can also handle Date objects correctly
// and has a defaults list for values you want stripped.
const cleanEmpty = function(obj, defaults = [undefined, null, NaN, '']) {
if (!defaults.length) return obj
if (defaults.includes(obj)) return
if (Array.isArray(obj))
return obj
.map(v => v && typeof v === 'object' ? cleanEmpty(v, defaults) : v)
.filter(v => !defaults.includes(v))
return Object.entries(obj).length
? Object.entries(obj)
.map(([k, v]) => ([k, v && typeof v === 'object' ? cleanEmpty(v, defaults) : v]))
.reduce((a, [k, v]) => (defaults.includes(v) ? a : { ...a, [k]: v}), {})
: obj
}
// testing
console.log('testing: undefined \n', cleanEmpty(undefined))
console.log('testing: null \n',cleanEmpty(null))
console.log('testing: NaN \n',cleanEmpty(NaN))
console.log('testing: empty string \n',cleanEmpty(''))
console.log('testing: empty array \n',cleanEmpty([]))
console.log('testing: date object \n',cleanEmpty(new Date(1589339052 * 1000)))
console.log('testing: nested empty arr \n',cleanEmpty({ 1: { 2 :null, 3: [] }}))
console.log('testing: comprehensive obj \n', cleanEmpty({
a: 5,
b: 0,
c: undefined,
d: {
e: null,
f: [{
a: undefined,
b: new Date(),
c: ''
}]
},
g: NaN,
h: null
}))
console.log('testing: different defaults \n', cleanEmpty({
a: 5,
b: 0,
c: undefined,
d: {
e: null,
f: [{
a: undefined,
b: '',
c: new Date()
}]
},
g: [0, 1, 2, 3, 4],
h: '',
}, [undefined, null]))
Here's an alternative
Typescript:
function objectDefined <T>(obj: T): T {
const acc: Partial<T> = {};
for (const key in obj) {
if (obj[key] !== undefined) acc[key] = obj[key];
}
return acc as T;
}
Javascript:
function objectDefined(obj) {
const acc = {};
for (const key in obj) {
if (obj[key] !== undefined) acc[key] = obj[key];
}
return acc;
}
If you want 4 lines of a pure ES7 solution:
const clean = e => e instanceof Object ? Object.entries(e).reduce((o, [k, v]) => {
if (typeof v === 'boolean' || v) o[k] = clean(v);
return o;
}, e instanceof Array ? [] : {}) : e;
Or if you prefer more readable version:
function filterEmpty(obj, [key, val]) {
if (typeof val === 'boolean' || val) {
obj[key] = clean(val)
};
return obj;
}
function clean(entry) {
if (entry instanceof Object) {
const type = entry instanceof Array ? [] : {};
const entries = Object.entries(entry);
return entries.reduce(filterEmpty, type);
}
return entry;
}
This will preserve boolean values and it will clean arrays too. It also preserves the original object by returning a cleaned copy.
a reduce helper can do the trick (without type checking) -
const cleanObj = Object.entries(objToClean).reduce((acc, [key, value]) => {
if (value) {
acc[key] = value;
}
return acc;
}, {});
If you don't want to mutate in place, but return a clone with the null/undefined removed, you could use the ES6 reduce function.
// Helper to remove undefined or null properties from an object
function removeEmpty(obj) {
// Protect against null/undefined object passed in
return Object.keys(obj || {}).reduce((x, k) => {
// Check for null or undefined
if (obj[k] != null) {
x[k] = obj[k];
}
return x;
}, {});
}
To piggypack on Ben's answer on how to solve this problem using lodash's _.pickBy, you can also solve this problem in the sister library: Underscore.js's _.pick.
var obj = {name: 'John', age: null};
var compacted = _.pick(obj, function(value) {
return value !== null && value !== undefined;
});
See: JSFiddle Example
You can also use ... spread syntax using forEach something like this:
let obj = { a: 1, b: "b", c: undefined, d: null };
let cleanObj = {};
Object.keys(obj).forEach(val => {
const newVal = obj[val];
cleanObj = newVal ? { ...cleanObj, [val]: newVal } : cleanObj;
});
console.info(cleanObj);
If someone needs to remove undefined values from an object with deep search using lodash then here is the code that I'm using. It's quite simple to modify it to remove all empty values (null/undefined).
function omitUndefinedDeep(obj) {
return _.reduce(obj, function(result, value, key) {
if (_.isObject(value)) {
result[key] = omitUndefinedDeep(value);
}
else if (!_.isUndefined(value)) {
result[key] = value;
}
return result;
}, {});
}
With Lodash:
_.omitBy({a: 1, b: null}, (v) => !v)
If you use eslint and want to avoid tripping the the no-param-reassign rule, you can use Object.assign in conjunction with .reduce and a computed property name for a fairly elegant ES6 solution:
const queryParams = { a: 'a', b: 'b', c: 'c', d: undefined, e: null, f: '', g: 0 };
const cleanParams = Object.keys(queryParams)
.filter(key => queryParams[key] != null)
.reduce((acc, key) => Object.assign(acc, { [key]: queryParams[key] }), {});
// { a: 'a', b: 'b', c: 'c', f: '', g: 0 }
Here is a functional way to remove nulls from an Object using ES6 without mutating the object using only reduce:
const stripNulls = (obj) => {
return Object.keys(obj).reduce((acc, current) => {
if (obj[current] !== null) {
return { ...acc, [current]: obj[current] }
}
return acc
}, {})
}
Recursively remove null, undefined, empty objects and empty arrays, returning a copy (ES6 version)
export function skipEmpties(dirty) {
let item;
if (Array.isArray(dirty)) {
item = dirty.map(x => skipEmpties(x)).filter(value => value !== undefined);
return item.length ? item : undefined;
} else if (dirty && typeof dirty === 'object') {
item = {};
Object.keys(dirty).forEach(key => {
const value = skipEmpties(dirty[key]);
if (value !== undefined) {
item[key] = value;
}
});
return Object.keys(item).length ? item : undefined;
} else {
return dirty === null ? undefined : dirty;
}
}
Related
I want to replace every string in and number in an object with an empty string of empty number.
For example:
const test = {
a: "asdf",
b: 2,
c: [
{
lala: "more strings",
d: "saaa"
}
]
}
with
const test = {
a: "",
b: 0,
c: [
{
lala: "",
d: ""
}
]
}
I can't think of an easy way to iterate over all the values in the object/array recursively. Is there a library (maybe lodash) that does this already?
A recursive implementation:
const test = { a: "asdf", b: 2, c: [{ lala: "more strings", d: "saaa", e: null }], g: 2.98 };
const isString = (val) => typeof val === 'string';
const isNumber = (val) => typeof val === 'number' && !Number.isNaN(val);
const isArray = (val) => Array.isArray(val);
const isObject = (val) => typeof val === 'object' && val !== null;
const modifyObject = (instance) => {
if (isString(instance)) return ""; //Base case
if (isNumber(instance)) return 0; //Base case
if (isArray(instance)) { //When instance is an Array we need to check further
return instance.map((item) => modifyObject(item));
}
if (isObject(instance)) { //When it's an object we need to recurse again to check if the value hits the base case or need to go further
return Object.entries(instance).reduce((acc, [k, v]) => ({
...acc,
[k]: modifyObject(v)
}), {});
}
return instance;
}
console.log(modifyObject(test));
Hope it helps!
The trickiest part would just be making sure you handle each type you could come across as you traverse an object/array.
Something like this should work:
function handleValue(val) {
if (typeof val === 'object')
if (val === null) {
return null
} else if (Array.isArray(val)) {
return handleArray(val)
} else {
return handleObject(val)
}
} else if (typeof val == "string") {
return ""
} else if (typeof val == 'number') {
return 0
}
}
function handleArray(arr) {
for (var i = 0; i < arr.length; i++) {
arr[i] = handleValue(arr[i])
}
return arr
}
function handleObject(obj) {
for (const [key, value] of Object.entries(obj)) {
obj[key] = handleValue(value)
}
return obj
}
You could use handleValue or handleObject on your example test object
Simple recursive solution:
const test = {
a: "asdf",
b: 2,
c: [{
lala: "more strings",
d: "saaa"
}]
}
const copy = replace(test)
console.log(copy)
function replace(value) {
if (typeof value === 'string') {
return ''
}
if (typeof value === 'number') {
return 0
}
if (typeof value !== 'object' || value === null) {
return value
}
if (Array.isArray(value)) {
return value.map(val => replace(val))
}
const obj = {}
for (const key in value) {
const val = value[key]
obj[key] = replace(val)
}
return obj
}
How do I remove all attributes which are undefined or null in a JavaScript object?
(Question is similar to this one for Arrays)
ES10/ES2019 examples
A simple one-liner (returning a new object).
let o = Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
Same as above but written as a function.
function removeEmpty(obj) {
return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
}
This function uses recursion to remove items from nested objects.
function removeEmpty(obj) {
return Object.fromEntries(
Object.entries(obj)
.filter(([_, v]) => v != null)
.map(([k, v]) => [k, v === Object(v) ? removeEmpty(v) : v])
);
}
ES6/ES2015 examples
A simple one-liner. Warning: This mutates the given object instead of returning a new one.
Object.keys(obj).forEach((k) => obj[k] == null && delete obj[k]);
A single declaration (not mutating the given object).
let o = Object.keys(obj)
.filter((k) => obj[k] != null)
.reduce((a, k) => ({ ...a, [k]: obj[k] }), {});
Same as above but written as a function.
function removeEmpty(obj) {
return Object.entries(obj)
.filter(([_, v]) => v != null)
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
}
This function uses recursion to remove items from nested objects.
function removeEmpty(obj) {
return Object.entries(obj)
.filter(([_, v]) => v != null)
.reduce(
(acc, [k, v]) => ({ ...acc, [k]: v === Object(v) ? removeEmpty(v) : v }),
{}
);
}
Same as the function above, but written in an imperative (non-functional) style.
function removeEmpty(obj) {
const newObj = {};
Object.entries(obj).forEach(([k, v]) => {
if (v === Object(v)) {
newObj[k] = removeEmpty(v);
} else if (v != null) {
newObj[k] = obj[k];
}
});
return newObj;
}
ES5/ES2009 examples
In the old days things were a lot more verbose.
This is a non recursive version written in a functional style.
function removeEmpty(obj) {
return Object.keys(obj)
.filter(function (k) {
return obj[k] != null;
})
.reduce(function (acc, k) {
acc[k] = obj[k];
return acc;
}, {});
}
This is a non recursive version written in an imperative style.
function removeEmpty(obj) {
const newObj = {};
Object.keys(obj).forEach(function (k) {
if (obj[k] && typeof obj[k] === "object") {
newObj[k] = removeEmpty(obj[k]);
} else if (obj[k] != null) {
newObj[k] = obj[k];
}
});
return newObj;
}
And a recursive version written in a functional style.
function removeEmpty(obj) {
return Object.keys(obj)
.filter(function (k) {
return obj[k] != null;
})
.reduce(function (acc, k) {
acc[k] = typeof obj[k] === "object" ? removeEmpty(obj[k]) : obj[k];
return acc;
}, {});
}
You can loop through the object:
var test = {
test1: null,
test2: 'somestring',
test3: 3,
}
function clean(obj) {
for (var propName in obj) {
if (obj[propName] === null || obj[propName] === undefined) {
delete obj[propName];
}
}
return obj
}
console.log(test);
console.log(clean(test));
If you're concerned about this property removal not running up object's proptype chain, you can also:
function clean(obj) {
var propNames = Object.getOwnPropertyNames(obj);
for (var i = 0; i < propNames.length; i++) {
var propName = propNames[i];
if (obj[propName] === null || obj[propName] === undefined) {
delete obj[propName];
}
}
}
A few notes on null vs undefined:
test.test1 === null; // true
test.test1 == null; // true
test.notaprop === null; // false
test.notaprop == null; // true
test.notaprop === undefined; // true
test.notaprop == undefined; // true
Shortest one liners for ES6+
Filter all falsy values ( "", 0, false, null, undefined )
Object.entries(obj).reduce((a,[k,v]) => (v ? (a[k]=v, a) : a), {})
Filter null and undefined values:
Object.entries(obj).reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {})
Filter ONLY null
Object.entries(obj).reduce((a,[k,v]) => (v === null ? a : (a[k]=v, a)), {})
Filter ONLY undefined
Object.entries(obj).reduce((a,[k,v]) => (v === undefined ? a : (a[k]=v, a)), {})
Recursive Solutions: Filters null and undefined
For Objects:
const cleanEmpty = obj => Object.entries(obj)
.map(([k,v])=>[k,v && typeof v === "object" ? cleanEmpty(v) : v])
.reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {});
For Objects and Arrays:
const cleanEmpty = obj => {
if (Array.isArray(obj)) {
return obj
.map(v => (v && typeof v === 'object') ? cleanEmpty(v) : v)
.filter(v => !(v == null));
} else {
return Object.entries(obj)
.map(([k, v]) => [k, v && typeof v === 'object' ? cleanEmpty(v) : v])
.reduce((a, [k, v]) => (v == null ? a : (a[k]=v, a)), {});
}
}
If you are using lodash or underscore.js, here is a simple solution:
var obj = {name: 'John', age: null};
var compacted = _.pickBy(obj);
This will only work with lodash 4, pre lodash 4 or underscore.js, use _.pick(obj, _.identity);
If somebody needs a recursive version of Owen's (and Eric's) answer, here it is:
/**
* Delete all null (or undefined) properties from an object.
* Set 'recurse' to true if you also want to delete properties in nested objects.
*/
function delete_null_properties(test, recurse) {
for (var i in test) {
if (test[i] === null) {
delete test[i];
} else if (recurse && typeof test[i] === 'object') {
delete_null_properties(test[i], recurse);
}
}
}
JSON.stringify removes the undefined keys.
removeUndefined = function(json){
return JSON.parse(JSON.stringify(json))
}
You can use a combination of JSON.stringify, its replacer parameter, and JSON.parse to turn it back into an object. Using this method also means the replacement is done to all nested keys within nested objects.
Example Object
var exampleObject = {
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3],
object: {
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3]
},
arrayOfObjects: [
{
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3]
},
{
string: 'value',
emptyString: '',
integer: 0,
nullValue: null,
array: [1, 2, 3]
}
]
};
Replacer Function
function replaceUndefinedOrNull(key, value) {
if (value === null || value === undefined) {
return undefined;
}
return value;
}
Clean the Object
exampleObject = JSON.stringify(exampleObject, replaceUndefinedOrNull);
exampleObject = JSON.parse(exampleObject);
CodePen example
Simplest possible Lodash solution to return an object with the null and undefined values filtered out.
_.omitBy(obj, _.isNil)
You can do a recursive removal in one line using json.stringify's replacer argument
const removeEmptyValues = obj => (
JSON.parse(JSON.stringify(obj, (k,v) => v ?? undefined))
)
Usage:
removeEmptyValues({a:{x:1,y:null,z:undefined}}) // Returns {a:{x:1}}
As mentioned in Emmanuel's comment, this technique only worked if your data structure contains only data types that can be put into JSON format (strings, numbers, lists, etc).
(This answer has been updated to use the new Nullish Coalescing operator. depending on browser support needs you may want to use this function instead: (k,v) => v!=null ? v : undefined)
You are probably looking for the delete keyword.
var obj = { };
obj.theProperty = 1;
delete obj.theProperty;
you can do shorter with ! condition
var r = {a: null, b: undefined, c:1};
for(var k in r)
if(!r[k]) delete r[k];
Remember in usage : as #semicolor announce in comments: This would also delete properties if the value is an empty string, false or zero
Remove all the properties with null and undefined
let obj = {
"id": 1,
"firstName": null,
"lastName": null,
"address": undefined,
"role": "customer",
"photo": "fb79fd5d-06c9-4097-8fdc-6cebf73fab26/fc8efe82-2af4-4c81-bde7-8d2f9dd7994a.jpg",
"location": null,
"idNumber": null,
};
let result = Object.entries(obj).reduce((a,[k,v]) => (v == null ? a : (a[k]=v, a)), {});
console.log(result)
I have same scenario in my project and achieved using following method.
It works with all data types, few mentioned above doesn't work with date and empty arrays .
removeEmptyKeysFromObject.js
removeEmptyKeysFromObject(obj) {
Object.keys(obj).forEach(key => {
if (Object.prototype.toString.call(obj[key]) === '[object Date]' && (obj[key].toString().length === 0 || obj[key].toString() === 'Invalid Date')) {
delete obj[key];
} else if (obj[key] && typeof obj[key] === 'object') {
this.removeEmptyKeysFromObject(obj[key]);
} else if (obj[key] == null || obj[key] === '') {
delete obj[key];
}
if (obj[key]
&& typeof obj[key] === 'object'
&& Object.keys(obj[key]).length === 0
&& Object.prototype.toString.call(obj[key]) !== '[object Date]') {
delete obj[key];
}
});
return obj;
}
pass any object to this function removeEmptyKeysFromObject()
Using ramda#pickBy you will remove all null, undefined and false values:
const obj = {a:1, b: undefined, c: null, d: 1}
R.pickBy(R.identity, obj)
As #manroe pointed out, to keep false values use isNil():
const obj = {a:1, b: undefined, c: null, d: 1, e: false}
R.pickBy(v => !R.isNil(v), obj)
Shorter ES6 pure solution, convert it to an array, use the filter function and convert it back to an object.
Would also be easy to make a function...
Btw. with this .length > 0 i check if there is an empty string / array, so it will remove empty keys.
const MY_OBJECT = { f: 'te', a: [] }
Object.keys(MY_OBJECT)
.filter(f => !!MY_OBJECT[f] && MY_OBJECT[f].length > 0)
.reduce((r, i) => { r[i] = MY_OBJECT[i]; return r; }, {});
JS BIN https://jsbin.com/kugoyinora/edit?js,console
Functional and immutable approach, without .filter and without creating more objects than needed
Object.keys(obj).reduce((acc, key) => (obj[key] === undefined ? acc : {...acc, [key]: obj[key]}), {})
Instead of delete the property, you can also create a new object with the keys that are not null.
const removeEmpty = (obj) => {
return Object.keys(obj).filter(key => obj[key]).reduce(
(newObj, key) => {
newObj[key] = obj[key]
return newObj
}, {}
)
}
For a deep search I used the following code, maybe it will be useful for anyone looking at this question (it is not usable for cyclic dependencies ) :
function removeEmptyValues(obj) {
for (var propName in obj) {
if (!obj[propName] || obj[propName].length === 0) {
delete obj[propName];
} else if (typeof obj[propName] === 'object') {
removeEmptyValues(obj[propName]);
}
}
return obj;
}
Here is a comprehensive recursive function (originally based on the one by #chickens) that will:
recursively remove what you tell it to defaults=[undefined, null, '', NaN]
Correctly handle regular objects, arrays and Date objects
const cleanEmpty = function(obj, defaults = [undefined, null, NaN, '']) {
if (!defaults.length) return obj
if (defaults.includes(obj)) return
if (Array.isArray(obj))
return obj
.map(v => v && typeof v === 'object' ? cleanEmpty(v, defaults) : v)
.filter(v => !defaults.includes(v))
return Object.entries(obj).length
? Object.entries(obj)
.map(([k, v]) => ([k, v && typeof v === 'object' ? cleanEmpty(v, defaults) : v]))
.reduce((a, [k, v]) => (defaults.includes(v) ? a : { ...a, [k]: v}), {})
: obj
}
USAGE:
// based off the recursive cleanEmpty function by #chickens.
// This one can also handle Date objects correctly
// and has a defaults list for values you want stripped.
const cleanEmpty = function(obj, defaults = [undefined, null, NaN, '']) {
if (!defaults.length) return obj
if (defaults.includes(obj)) return
if (Array.isArray(obj))
return obj
.map(v => v && typeof v === 'object' ? cleanEmpty(v, defaults) : v)
.filter(v => !defaults.includes(v))
return Object.entries(obj).length
? Object.entries(obj)
.map(([k, v]) => ([k, v && typeof v === 'object' ? cleanEmpty(v, defaults) : v]))
.reduce((a, [k, v]) => (defaults.includes(v) ? a : { ...a, [k]: v}), {})
: obj
}
// testing
console.log('testing: undefined \n', cleanEmpty(undefined))
console.log('testing: null \n',cleanEmpty(null))
console.log('testing: NaN \n',cleanEmpty(NaN))
console.log('testing: empty string \n',cleanEmpty(''))
console.log('testing: empty array \n',cleanEmpty([]))
console.log('testing: date object \n',cleanEmpty(new Date(1589339052 * 1000)))
console.log('testing: nested empty arr \n',cleanEmpty({ 1: { 2 :null, 3: [] }}))
console.log('testing: comprehensive obj \n', cleanEmpty({
a: 5,
b: 0,
c: undefined,
d: {
e: null,
f: [{
a: undefined,
b: new Date(),
c: ''
}]
},
g: NaN,
h: null
}))
console.log('testing: different defaults \n', cleanEmpty({
a: 5,
b: 0,
c: undefined,
d: {
e: null,
f: [{
a: undefined,
b: '',
c: new Date()
}]
},
g: [0, 1, 2, 3, 4],
h: '',
}, [undefined, null]))
Here's an alternative
Typescript:
function objectDefined <T>(obj: T): T {
const acc: Partial<T> = {};
for (const key in obj) {
if (obj[key] !== undefined) acc[key] = obj[key];
}
return acc as T;
}
Javascript:
function objectDefined(obj) {
const acc = {};
for (const key in obj) {
if (obj[key] !== undefined) acc[key] = obj[key];
}
return acc;
}
If you want 4 lines of a pure ES7 solution:
const clean = e => e instanceof Object ? Object.entries(e).reduce((o, [k, v]) => {
if (typeof v === 'boolean' || v) o[k] = clean(v);
return o;
}, e instanceof Array ? [] : {}) : e;
Or if you prefer more readable version:
function filterEmpty(obj, [key, val]) {
if (typeof val === 'boolean' || val) {
obj[key] = clean(val)
};
return obj;
}
function clean(entry) {
if (entry instanceof Object) {
const type = entry instanceof Array ? [] : {};
const entries = Object.entries(entry);
return entries.reduce(filterEmpty, type);
}
return entry;
}
This will preserve boolean values and it will clean arrays too. It also preserves the original object by returning a cleaned copy.
a reduce helper can do the trick (without type checking) -
const cleanObj = Object.entries(objToClean).reduce((acc, [key, value]) => {
if (value) {
acc[key] = value;
}
return acc;
}, {});
If you don't want to mutate in place, but return a clone with the null/undefined removed, you could use the ES6 reduce function.
// Helper to remove undefined or null properties from an object
function removeEmpty(obj) {
// Protect against null/undefined object passed in
return Object.keys(obj || {}).reduce((x, k) => {
// Check for null or undefined
if (obj[k] != null) {
x[k] = obj[k];
}
return x;
}, {});
}
To piggypack on Ben's answer on how to solve this problem using lodash's _.pickBy, you can also solve this problem in the sister library: Underscore.js's _.pick.
var obj = {name: 'John', age: null};
var compacted = _.pick(obj, function(value) {
return value !== null && value !== undefined;
});
See: JSFiddle Example
You can also use ... spread syntax using forEach something like this:
let obj = { a: 1, b: "b", c: undefined, d: null };
let cleanObj = {};
Object.keys(obj).forEach(val => {
const newVal = obj[val];
cleanObj = newVal ? { ...cleanObj, [val]: newVal } : cleanObj;
});
console.info(cleanObj);
If someone needs to remove undefined values from an object with deep search using lodash then here is the code that I'm using. It's quite simple to modify it to remove all empty values (null/undefined).
function omitUndefinedDeep(obj) {
return _.reduce(obj, function(result, value, key) {
if (_.isObject(value)) {
result[key] = omitUndefinedDeep(value);
}
else if (!_.isUndefined(value)) {
result[key] = value;
}
return result;
}, {});
}
With Lodash:
_.omitBy({a: 1, b: null}, (v) => !v)
If you use eslint and want to avoid tripping the the no-param-reassign rule, you can use Object.assign in conjunction with .reduce and a computed property name for a fairly elegant ES6 solution:
const queryParams = { a: 'a', b: 'b', c: 'c', d: undefined, e: null, f: '', g: 0 };
const cleanParams = Object.keys(queryParams)
.filter(key => queryParams[key] != null)
.reduce((acc, key) => Object.assign(acc, { [key]: queryParams[key] }), {});
// { a: 'a', b: 'b', c: 'c', f: '', g: 0 }
Here is a functional way to remove nulls from an Object using ES6 without mutating the object using only reduce:
const stripNulls = (obj) => {
return Object.keys(obj).reduce((acc, current) => {
if (obj[current] !== null) {
return { ...acc, [current]: obj[current] }
}
return acc
}, {})
}
Recursively remove null, undefined, empty objects and empty arrays, returning a copy (ES6 version)
export function skipEmpties(dirty) {
let item;
if (Array.isArray(dirty)) {
item = dirty.map(x => skipEmpties(x)).filter(value => value !== undefined);
return item.length ? item : undefined;
} else if (dirty && typeof dirty === 'object') {
item = {};
Object.keys(dirty).forEach(key => {
const value = skipEmpties(dirty[key]);
if (value !== undefined) {
item[key] = value;
}
});
return Object.keys(item).length ? item : undefined;
} else {
return dirty === null ? undefined : dirty;
}
}
I've written the following function which cleans nulls and undefined from an object, but for some reason it also removes key where the value is 0.
For example:
{
key1: 'value1',
key2: 0
}
The function will remove key2, even though it should not.
Here is the function:
const cleanJson = function cleanJson (obj) {
if (Object.prototype.toString.call(obj) !== '[object Object]') return obj;
return Object.keys(obj).filter(key => obj[key] && obj[key] !== 'delete').reduce((newObj, key) => {
newObj[key] = Object.prototype.toString.call(obj[key]) !== '[object Object]' ? obj[key] : cleanJson(obj[key]);
return newObj;
}, {});
};
Please advise.
Here is one option using Object.entries:
const myJSON = {key1:'value1', key2:0, key3:null, key4:undefined, key5:""};
const myCleanJSON = Object.entries(myJSON)
.filter(([key, value]) => (value !== null && typeof value !== 'undefined'))
.reduce((acc, b) => ((!acc.length) ? {...acc, [b[0]]: b[1] } : { [acc[0]] : acc[1], [b[0]]: b[1] }));
console.log(myCleanJSON);
The problem is within
obj[key] && obj[key] !== 'delete'
When obj[key] == 0, would return false.
Because 0 (as well as the empty string "") evaluates to false when cast to a Boolean, you need to explicitly check for undefined and null:
let a = {
key1: 'value1',
key2: 0,
key3: null
}
function cleanProps(o) {
if (Object.prototype.toString.call(o) !== '[object Object]') return o
for (key in o) {
if([undefined, null].includes(o[key])) delete o[key]
}
return o
}
console.log(cleanProps(a))
I think that would solve your issue:
obj[key] !== null && obj[key] !== undefined && obj[key] !== 'delete'
This is a question similar to How to remove undefined and null values from an object using lodash?. However, the solutions proposed there do not conserve the constructor. In addition to that, I want to only delete those keys which starts, say with '_'.
Here is what I am looking for, and can't seem to manage to get from lodash :
Input : new Cons({key1 : 'value1', key2 : {key21 : 'value21', _key22: undefined}, key3: undefined, _key4 : undefined})
Output :
{key1 : 'value1', key2 : {key21 : 'value21'}, key3: undefined}
where for example function Cons(obj){_.extend(this, obj)}
I have a solution with omitBy using lodash, however, I loose the constructor information (i.e. I cannot use instanceof Cons anymore to discriminate the object constructor). forIn looked like a good candidate for the recursive traversal but it only provides me with the value and the key. I also need the path in order to delete the object (with unset).
Please note that:
the object is any valid javascript object
the constructor is any javascript valid constructor, and the object comes with the constructor already set.
the resulting object must have instanceof whatevertheconstructorwas still true
Is there a better solution (with lodash or else)?
You can create a function that recursively omits keys through the use of omitBy() and mapValues() as an assisting function for traversing keys recursively. Also note that this also supports array traversal for objects with nested arrays or top level arrays with nested objects.
function omitByRecursively(value, iteratee) {
var cb = v => omitByRecursively(v, iteratee);
return _.isObject(value)
? _.isArray(value)
? _.map(value, cb)
: _(value).omitBy(iteratee).mapValues(cb).value()
: value;
}
function Cons(obj) {
_.extend(this, omitByRecursively(obj, (v, k) => k[0] === '_'));
}
Example:
function omitByRecursively(value, iteratee) {
var cb = v => omitByRecursively(v, iteratee);
return _.isObject(value)
? _.isArray(value)
? _.map(value, cb)
: _(value).omitBy(iteratee).mapValues(cb).value()
: value;
}
function Cons(obj) {
_.extend(this, omitByRecursively(obj, (v, k) => k[0] === '_'));
}
var result = new Cons({
key1 : 'value1',
key2 : {
key21 : 'value21',
_key22: undefined
},
key3: undefined,
_key4 : undefined,
key5: [
{
_key: 'value xx',
key7: 'value zz',
_key8: 'value aa'
}
]
});
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.12.0/lodash.js"></script>
Update
You can mutate the object itself by creating a function that recursively traverses the object with each() and settles the removal by unset().
function omitByRecursivelyInPlace(value, iteratee) {
_.each(value, (v, k) => {
if(iteratee(v, k)) {
_.unset(value, k);
} else if(_.isObject(v)) {
omitByRecursivelyInPlace(v, iteratee);
}
});
return value;
}
function Cons(obj){_.extend(this, obj)}
var result = omitByRecursivelyInPlace(instance, (v, k) => k[0] === '_');
function omitByRecursivelyInPlace(value, iteratee) {
_.each(value, (v, k) => {
if(iteratee(v, k)) {
_.unset(value, k);
} else if(_.isObject(v)) {
omitByRecursivelyInPlace(v, iteratee);
}
});
return value;
}
function Cons(obj){_.extend(this, obj)}
var instance = new Cons({
key1 : 'value1',
key2 : {
key21 : 'value21',
_key22: undefined
},
key3: undefined,
_key4 : undefined,
key5: [
{
_key: 'value xx',
key7: 'value zz',
_key8: 'value aa'
}
]
});
var result = omitByRecursivelyInPlace(instance, (v, k) => k[0] === '_');
console.log(result instanceof Cons);
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.12.0/lodash.js"></script>
You can use the rundef package.
By default, it will replace all top-level properties with values set to undefined. However it supports the following options:
mutate - set this to false to return the same object that you have provided; this will ensure the constructor is not changed
recursive - set this to true to recursively process your object
Therefore, for your use case, you can run:
rundef(object, false, true)
You can use JSON.stringify(), JSON.parse(), RegExp.prototype.test()
JSON.parse(JSON.stringify(obj, function(a, b) {
if (!/^_/.test(a) && b === undefined) {
return null
}
return /^_/.test(a) && !b ? void 0 : b
}).replace(/null/g, '"undefined"'));
var obj = {key1 : 'value1', key2 : {key21 : 'value21', _key22: undefined}, key3: undefined, _key4 : undefined}
var res = JSON.stringify(obj, function(a, b) {
if (!/^_/.test(a) && b === undefined) {
return null
}
return /^_/.test(a) && !b ? void 0 : b
}).replace(/null/g, '"undefined"');
document.querySelector("pre").textContent = res;
res = JSON.parse(res);
console.log(res)
<pre></pre>
Disclaimer: I have no idea what lodash supports as built-in functions, but this is very easy to implement with vanilla javascript.
Start with a generic function for filtering your object's keys
// filter obj using function f
// this works just like Array.prototype.filter, except on objects
// f receives (value, key, obj) for each object key
// if f returns true, the key:value appears in the result
// if f returns false, the key:value is skipped
const filterObject = f=> obj=>
Object.keys(obj).reduce((res,k)=>
f(obj[k], k, obj) ? Object.assign(res, {[k]: obj[k]}) : res
, {});
Then a function that filters based on your specific behavior
// filter out keys starting with `_` that have null or undefined values
const filterBadKeys = filterObject((v,k)=> /^[^_]/.test(k) || v !== null && v !== undefined);
Then call it on an object
filterBadKeys({a: null, _b: null, _c: undefined, z: 1});
//=> { a: null, z: 1 }
This can be easily integrated into your constructor now
function Cons(obj) {
_.extend(this, filterBadKeys(obj));
// ...
}
EDIT:
On second thought, instead of butchering a perfectly good function with implicit deep recursion, you could abstract out the generic operations and define a specific "deep" filtering function
const reduceObject = f=> init=> obj=>
Object.keys(obj).reduce((res,k)=> f(res, obj[k], k, obj), init);
// shallow filter
const filterObject = f=>
reduceObject ((res, v, k, obj)=> f(v, k, obj) ? Object.assign(res, {[k]: v}) : res) ({});
// deep filter
const deepFilterObject = f=>
reduceObject ((res, v, k, obj)=> {
if (f(v, k, obj))
if (v && v.constructor === Object)
return Object.assign(res, {[k]: deepFilterObject (f) (v)});
else
return Object.assign(res, {[k]: v});
else
return res;
}) ({});
const filterBadKeys = deepFilterObject((v,k)=> /^[^_]/.test(k) || v !== null && v !== undefined);
filterBadKeys({a: null, _b: null, _c: undefined, _d: { e: 1, _f: null }, z: 2});
//=> { a: null, _d: { e: 1 }, z: 2 }
Integration into your constructor stays the same
function Cons(obj) {
_.extend(this, filterBadKeys(obj));
// ...
}
#ryeballar's answer mostly worked, except I wanted three additional features:
first recurse, then do the iteratee check, since after recursing on the object it is possible that it should be omitted
have it work with arrays
some typing
Also took some ideas from: https://codereview.stackexchange.com/a/58279
export function omitByRecursivelyInPlace<T extends object | null | undefined>(value: T, iteratee: (v: any, k: string) => any) {
_.each(value, (v, k) => {
// no longer an if/else
if (_.isObject(v)) {
omitByRecursivelyInPlace(v, iteratee);
}
if (iteratee(v, k)) {
// check for array types
if (_.isArray(value)) _.pullAt(value, [parseInt(k)]);
else _.unset(value, k);
}
});
return value;
}
Not sure about the performance here. Open to feedback. May not be exactly what the OP is asking for.
See https://github.com/lodash/lodash/issues/723 for discussion around this topic in the official lodash repo. Looks like it won't be supported.
I need to flatten a nested object. Need a one liner. Not sure what the correct term for this process is.
I can use pure Javascript or libraries, I particularly like underscore.
I've got ...
{
a:2,
b: {
c:3
}
}
And I want ...
{
a:2,
c:3
}
I've tried ...
var obj = {"fred":2,"jill":4,"obby":{"john":5}};
var resultObj = _.pick(obj, "fred")
alert(JSON.stringify(resultObj));
Which works but I also need this to work ...
var obj = {"fred":2,"jill":4,"obby":{"john":5}};
var resultObj = _.pick(obj, "john")
alert(JSON.stringify(resultObj));
Here you go:
Object.assign({}, ...function _flatten(o) { return [].concat(...Object.keys(o).map(k => typeof o[k] === 'object' ? _flatten(o[k]) : ({[k]: o[k]})))}(yourObject))
Summary: recursively create an array of one-property objects, then combine them all with Object.assign.
This uses ES6 features including Object.assign or the spread operator, but it should be easy enough to rewrite not to require them.
For those who don't care about the one-line craziness and would prefer to be able to actually read it (depending on your definition of readability):
Object.assign(
{},
...function _flatten(o) {
return [].concat(...Object.keys(o)
.map(k =>
typeof o[k] === 'object' ?
_flatten(o[k]) :
({[k]: o[k]})
)
);
}(yourObject)
)
Simplified readable example, no dependencies
/**
* Flatten a multidimensional object
*
* For example:
* flattenObject{ a: 1, b: { c: 2 } }
* Returns:
* { a: 1, c: 2}
*/
export const flattenObject = (obj) => {
const flattened = {}
Object.keys(obj).forEach((key) => {
const value = obj[key]
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
Object.assign(flattened, flattenObject(value))
} else {
flattened[key] = value
}
})
return flattened
}
Features
No dependencies
Works with null values
Works with arrays
Working example https://jsfiddle.net/webbertakken/jn613d8p/26/
Here is a true, crazy one-liner that flats the nested object recursively:
const flatten = (obj, roots=[], sep='.') => Object.keys(obj).reduce((memo, prop) => Object.assign({}, memo, Object.prototype.toString.call(obj[prop]) === '[object Object]' ? flatten(obj[prop], roots.concat([prop]), sep) : {[roots.concat([prop]).join(sep)]: obj[prop]}), {})
Multiline version, explained:
// $roots keeps previous parent properties as they will be added as a prefix for each prop.
// $sep is just a preference if you want to seperate nested paths other than dot.
const flatten = (obj, roots = [], sep = '.') => Object
// find props of given object
.keys(obj)
// return an object by iterating props
.reduce((memo, prop) => Object.assign(
// create a new object
{},
// include previously returned object
memo,
Object.prototype.toString.call(obj[prop]) === '[object Object]'
// keep working if value is an object
? flatten(obj[prop], roots.concat([prop]), sep)
// include current prop and value and prefix prop with the roots
: {[roots.concat([prop]).join(sep)]: obj[prop]}
), {})
An example:
const obj = {a: 1, b: 'b', d: {dd: 'Y'}, e: {f: {g: 'g'}}}
const flat = flatten(obj)
{
'a': 1,
'b': 'b',
'd.dd': 'Y',
'e.f.g': 'g'
}
Happy one-liner day!
ES6 Native, Recursive:
One-liner
const crushObj = (obj) => Object.keys(obj).reduce((acc, cur) => typeof obj[cur] === 'object' ? { ...acc, ...crushObj(obj[cur]) } : { ...acc, [cur]: obj[cur] } , {})
Expanded
const crushObj = (obj = {}) => Object.keys(obj || {}).reduce((acc, cur) => {
if (typeof obj[cur] === 'object') {
acc = { ...acc, ...crushObj(obj[cur])}
} else { acc[cur] = obj[cur] }
return acc
}, {})
Usage
const obj = {
a:2,
b: {
c:3
}
}
const crushed = crushObj(obj)
console.log(crushed)
// { a: 2, c: 3 }
My ES6 version:
const flatten = (obj) => {
let res = {};
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'object') {
res = { ...res, ...flatten(value) };
} else {
res[key] = value;
}
}
return res;
}
It's not quite a one liner, but here's a solution that doesn't require anything from ES6. It uses underscore's extend method, which could be swapped out for jQuery's.
function flatten(obj) {
var flattenedObj = {};
Object.keys(obj).forEach(function(key){
if (typeof obj[key] === 'object') {
$.extend(flattenedObj, flatten(obj[key]));
} else {
flattenedObj[key] = obj[key];
}
});
return flattenedObj;
}
I like this code because it's a bit easier to understand.
Edit: I added some functionality I needed, so now it's a bit harder to understand.
const data = {
a: "a",
b: {
c: "c",
d: {
e: "e",
f: [
"g",
{
i: "i",
j: {},
k: []
}
]
}
}
};
function flatten(data, response = {}, flatKey = "", onlyLastKey = false) {
for (const [key, value] of Object.entries(data)) {
let newFlatKey;
if (!isNaN(parseInt(key)) && flatKey.includes("[]")) {
newFlatKey = (flatKey.charAt(flatKey.length - 1) == "." ? flatKey.slice(0, -1) : flatKey) + `[${key}]`;
} else if (!flatKey.includes(".") && flatKey.length > 0) {
newFlatKey = `${flatKey}.${key}`;
} else {
newFlatKey = `${flatKey}${key}`;
}
if (typeof value === "object" && value !== null && Object.keys(value).length > 0) {
flatten(value, response, `${newFlatKey}.`, onlyLastKey);
} else {
if(onlyLastKey){
newFlatKey = newFlatKey.split(".").pop();
}
if (Array.isArray(response)) {
response.push({
[newFlatKey.replace("[]", "")]: value
});
} else {
response[newFlatKey.replace("[]", "")] = value;
}
}
}
return response;
}
console.log(flatten(data));
console.log(flatten(data, {}, "data"));
console.log(flatten(data, {}, "data[]"));
console.log(flatten(data, {}, "data", true));
console.log(flatten(data, {}, "data[]", true));
console.log(flatten(data, []));
console.log(flatten(data, [], "data"));
console.log(flatten(data, [], "data[]"));
console.log(flatten(data, [], "data", true));
console.log(flatten(data, [], "data[]", true));
Demo https://stackblitz.com/edit/typescript-flatter
For insinde a typescript class use:
function flatten(data: any, response = {}, flatKey = "", onlyLastKey = false) {
for (const [key, value] of Object.entries(data)) {
let newFlatKey: string;
if (!isNaN(parseInt(key)) && flatKey.includes("[]")) {
newFlatKey = (flatKey.charAt(flatKey.length - 1) == "." ? flatKey.slice(0, -1) : flatKey) + `[${key}]`;
} else if (!flatKey.includes(".") && flatKey.length > 0) {
newFlatKey = `${flatKey}.${key}`;
} else {
newFlatKey = `${flatKey}${key}`;
}
if (typeof value === "object" && value !== null && Object.keys(value).length > 0) {
flatten(value, response, `${newFlatKey}.`, onlyLastKey);
} else {
if(onlyLastKey){
newFlatKey = newFlatKey.split(".").pop();
}
if (Array.isArray(response)) {
response.push({
[newFlatKey.replace("[]", "")]: value
});
} else {
response[newFlatKey.replace("[]", "")] = value;
}
}
}
return response;
}
Here's an ES6 version in TypeScript. It takes the best of answers given here and elsewhere. Some features:
Supports Date objects and converts them into ISO strings
Puts an underscore between the parent's and child's key (e.g. {a: {b: 'test'}} becomes {a_b: 'test'}
const flatten = (obj: Record<string, unknown>, parent?: string): Record<string, unknown> => {
let res: Record<string, unknown> = {}
for (const [key, value] of Object.entries(obj)) {
const propName = parent ? parent + '_' + key : key
const flattened: Record<string, unknown> = {}
if (value instanceof Date) {
flattened[key] = value.toISOString()
} else if(typeof value === 'object' && value !== null){
res = {...res, ...flatten(value as Record<string, unknown>, propName)}
} else {
res[propName] = value
}
}
return res
}
An example:
const example = {
person: {
firstName: 'Demo',
lastName: 'Person'
},
date: new Date(),
hello: 'world'
}
// becomes
const flattenedExample = {
person_firstName: 'Demo',
person_lastName: 'Person',
date: '2021-10-18T10:41:14.278Z',
hello: 'world'
}
This is a function I've got in my common libraries for exactly this purpose.
I believe I got this from a similar stackoverflow question, but cannot remember which (edit: Fastest way to flatten / un-flatten nested JSON objects - Thanks Yoshi!)
function flatten(data) {
var result = {};
function recurse (cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop + "[" + i + "]");
if (l == 0)
result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
}
if (isEmpty && prop)
result[prop] = {};
}
}
recurse(data, "");
return result;
}
This can then be called as follows:
var myJSON = '{a:2, b:{c:3}}';
var myFlattenedJSON = flatten(myJSON);
You can also append this function to the standard Javascript string class as follows:
String.prototype.flattenJSON = function() {
var data = this;
var result = {};
function recurse (cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop + "[" + i + "]");
if (l == 0)
result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
}
if (isEmpty && prop)
result[prop] = {};
}
}
recurse(data, "");
return result;
}
With which, you can do the following:
var flattenedJSON = '{a:2, b:{c:3}}'.flattenJSON();
Here are vanilla solutions that work for arrays, primitives, regular expressions, functions, any number of nested object levels, and just about everything else I could throw at them. The first overwrites property values in the manner that you would expect from Object.assign.
((o) => {
return o !== Object(o) || Array.isArray(o) ? {}
: Object.assign({}, ...function leaves(o) {
return [].concat.apply([], Object.entries(o)
.map(([k, v]) => {
return (( !v || typeof v !== 'object'
|| !Object.keys(v).some(key => v.hasOwnProperty(key))
|| Array.isArray(v))
? {[k]: v}
: leaves(v)
);
})
);
}(o))
})(o)
The second accumulates values into an array.
((o) => {
return o !== Object(o) || Array.isArray(o) ? {}
: (function () {
return Object.values((function leaves(o) {
return [].concat.apply([], !o ? [] : Object.entries(o)
.map(([k, v]) => {
return (( !v || typeof v !== 'object'
|| !Object.keys(v).some(k => v.hasOwnProperty(k))
|| (Array.isArray(v) && !v.some(el => typeof el === 'object')))
? {[k]: v}
: leaves(v)
);
})
);
}(o))).reduce((acc, cur) => {
return ((key) => {
acc[key] = !acc[key] ? [cur[key]]
: new Array(...new Set(acc[key].concat([cur[key]])))
})(Object.keys(cur)[0]) ? acc : acc
}, {})
})(o);
})(o)
Also please do not include code like this in production as it is terribly difficult to debug.
function leaves1(o) {
return ((o) => {
return o !== Object(o) || Array.isArray(o) ? {}
: Object.assign({}, ...function leaves(o) {
return [].concat.apply([], Object.entries(o)
.map(([k, v]) => {
return (( !v || typeof v !== 'object'
|| !Object.keys(v).some(key => v.hasOwnProperty(key))
|| Array.isArray(v))
? {[k]: v}
: leaves(v)
);
})
);
}(o))
})(o);
}
function leaves2(o) {
return ((o) => {
return o !== Object(o) || Array.isArray(o) ? {}
: (function () {
return Object.values((function leaves(o) {
return [].concat.apply([], !o ? [] : Object.entries(o)
.map(([k, v]) => {
return (( !v || typeof v !== 'object'
|| !Object.keys(v).some(k => v.hasOwnProperty(k))
|| (Array.isArray(v) && !v.some(el => typeof el === 'object')))
? {[k]: v}
: leaves(v)
);
})
);
}(o))).reduce((acc, cur) => {
return ((key) => {
acc[key] = !acc[key] ? [cur[key]]
: new Array(...new Set(acc[key].concat([cur[key]])))
})(Object.keys(cur)[0]) ? acc : acc
}, {})
})(o);
})(o);
}
const obj = {
l1k0: 'foo',
l1k1: {
l2k0: 'bar',
l2k1: {
l3k0: {},
l3k1: null
},
l2k2: undefined
},
l1k2: 0,
l2k3: {
l3k2: true,
l3k3: {
l4k0: [1,2,3],
l4k1: [4,5,'six', {7: 'eight'}],
l4k2: {
null: 'test',
[{}]: 'obj',
[Array.prototype.map]: Array.prototype.map,
l5k3: ((o) => (typeof o === 'object'))(this.obj),
}
}
},
l1k4: '',
l1k5: new RegExp(/[\s\t]+/g),
l1k6: function(o) { return o.reduce((a,b) => a+b)},
false: [],
}
const objs = [null, undefined, {}, [], ['non', 'empty'], 42, /[\s\t]+/g, obj];
objs.forEach(o => {
console.log(leaves1(o));
});
objs.forEach(o => {
console.log(leaves2(o));
});
Here is an actual oneliner of just 91 characters, using Underscore. (Of course. What else?)
var { reduce, isObject } = _;
var data = {
a: 1,
b: 2,
c: {
d: 3,
e: 4,
f: {
g: 5
},
h: 6
}
};
var tip = (v, m={}) => reduce(v, (m, v, k) => isObject(v) ? tip(v, m) : {...m, [k]: v}, m);
console.log(tip(data));
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
Readable version:
var { reduce, isObject, extend } = _;
var data = {
a: 1,
b: 2,
c: {
d: 3,
e: 4,
f: {
g: 5
},
h: 6
}
};
// This function is passed to _.reduce below.
// We visit a single key of the input object. If the value
// itself is an object, we recursively copy its keys into
// the output object (memo) by calling tip. Otherwise we
// add the key-value pair to the output object directly.
function tipIteratee(memo, value, key) {
if (isObject(value)) return tip(value, memo);
return extend(memo, {[key]: value});
}
// The entry point of the algorithm. Walks over the keys of
// an object using _.reduce, collecting all tip keys in memo.
function tip(value, memo = {}) {
return _.reduce(value, tipIteratee, memo);
}
console.log(tip(data));
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
Also works with Lodash.
To flatten only the first level of the object and merge duplicate object keys into an array:
var myObj = {
id: '123',
props: {
Name: 'Apple',
Type: 'Fruit',
Link: 'apple.com',
id: '345'
},
moreprops: {
id: "466"
}
};
const flattenObject = (obj) => {
let flat = {};
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'object' && value !== null) {
for (const [subkey, subvalue] of Object.entries(value)) {
// avoid overwriting duplicate keys: merge instead into array
typeof flat[subkey] === 'undefined' ?
flat[subkey] = subvalue :
Array.isArray(flat[subkey]) ?
flat[subkey].push(subvalue) :
flat[subkey] = [flat[subkey], subvalue]
}
} else {
flat = {...flat, ...{[key]: value}};
}
}
return flat;
}
console.log(flattenObject(myObj))
Object.assign requires a polyfill. This version is similar to previous ones, but it is not using Object.assign and it is still keep tracking of parent's name
const flatten = (obj, parent = null) => Object.keys(obj).reduce((acc, cur) =>
typeof obj[cur] === 'object' ? { ...acc, ...flatten(obj[cur], cur) } :
{ ...acc, [((parent) ? parent + '.' : "") + cur]: obj[cur] } , {})
const obj = {
a:2,
b: {
c:3
}
}
const flattened = flatten(obj)
console.log(flattened)
Here is a flatten function that correctly outputs array indexes.
function flatten(obj) {
const result = {};
for (const key of Object.keys(obj)) {
if (typeof obj[key] === 'object') {
const nested = flatten(obj[key]);
for (const nestedKey of Object.keys(nested)) {
result[`${key}.${nestedKey}`] = nested[nestedKey];
}
} else {
result[key] = obj[key];
}
}
return result;
}
Example Input:
{
"first_name": "validations.required",
"no_middle_name": "validations.required",
"last_name": "validations.required",
"dob": "validations.required",
"citizenship": "validations.required",
"citizenship_identity": {
"name": "validations.required",
"value": "validations.required"
},
"address": [
{
"country_code": "validations.required",
"street": "validations.required",
"city": "validations.required",
"state": "validations.required",
"zipcode": "validations.required",
"start_date": "validations.required",
"end_date": "validations.required"
},
{
"country_code": "validations.required",
"street": "validations.required",
"city": "validations.required",
"state": "validations.required",
"zipcode": "validations.required",
"start_date": "validations.required",
"end_date": "validations.required"
}
]
}
Example Output:
const flattenedOutput = flatten(inputObj);
{
"first_name": "validations.required",
"no_middle_name": "validations.required",
"last_name": "validations.required",
"dob": "validations.required",
"citizenship": "validations.required",
"citizenship_identity.name": "validations.required",
"citizenship_identity.value": "validations.required",
"address.0.country_code": "validations.required",
"address.0.street": "validations.required",
"address.0.city": "validations.required",
"address.0.state": "validations.required",
"address.0.zipcode": "validations.required",
"address.0.start_date": "validations.required",
"address.0.end_date": "validations.required",
"address.1.country_code": "validations.required",
"address.1.street": "validations.required",
"address.1.city": "validations.required",
"address.1.state": "validations.required",
"address.1.zipcode": "validations.required",
"address.1.start_date": "validations.required",
"address.1.end_date": "validations.required"
}
function flatten(obj: any) {
return Object.keys(obj).reduce((acc, current) => {
const key = `${current}`;
const currentValue = obj[current];
if (Array.isArray(currentValue) || Object(currentValue) === currentValue) {
Object.assign(acc, flatten(currentValue));
} else {
acc[key] = currentValue;
}
return acc;
}, {});
};
let obj = {
a:2,
b: {
c:3
}
}
console.log(flatten(obj))
Demo
https://stackblitz.com/edit/typescript-flatten-json
Here goes, not thoroughly tested. Utilizes ES6 syntax too!!
loopValues(val){
let vals = Object.values(val);
let q = [];
vals.forEach(elm => {
if(elm === null || elm === undefined) { return; }
if (typeof elm === 'object') {
q = [...q, ...this.loopValues(elm)];
}
return q.push(elm);
});
return q;
}
let flatValues = this.loopValues(object)
flatValues = flatValues.filter(elm => typeof elm !== 'object');
console.log(flatValues);
I know its been very long, but it may be helpful for some one in the future
I've used recursion
let resObj = {};
function flattenObj(obj) {
for (let key in obj) {
if (!(typeof obj[key] == 'object')) {
// console.log('not an object', key);
resObj[key] = obj[key];
// console.log('res obj is ', resObj);
} else {
flattenObj(obj[key]);
}
}
return resObj;
}
Here's my TypeScript extension from #Webber's answer. Also supports dates:
private flattenObject(obj: any): any {
const flattened = {};
for (const key of Object.keys(obj)) {
if (isNullOrUndefined(obj[key])) {
continue;
}
if (typeof obj[key].getMonth === 'function') {
flattened[key] = (obj[key] as Date).toISOString();
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
Object.assign(flattened, this.flattenObject(obj[key]));
} else {
flattened[key] = obj[key];
}
}
return flattened;
}
const obj = {
a:2,
b: {
c:3
}
}
// recursive function for extracting keys
function extractKeys(obj) {
let flattenedObj = {};
for(let [key, value] of Object.entries(obj)){
if(typeof value === "object") {
flattenedObj = {...flattenedObj, ...extractKeys(value)};
} else {
flattenedObj[key] = value;
}
}
return flattenedObj;
}
// main code
let flattenedObj = extractKeys(obj);
console.log(flattenedObj);