Related
I need to deep reverse the order of all keys in a JavaScript object.
For example:
{
a: 'val1',
b: 'val2',
c: {
ca: 'val3',
cb: 'val4'
}
}
Must become:
{
c: {
cb: 'val4',
ca: 'val3'
},
b: 'val2',
a: 'val1'
}
I have tried writing a recursive function, but I cannot figure out how to fix it:
function reverseKeys (obj, newObj = {}) {
Object.keys(obj).reverse().forEach(key => {
if(typeof obj[key] === 'object') {
newObj = reverseKeys(obj[key], {})
}
newObj[key] = obj[key]
})
return newObj
}
Solution inspired by #Unmitigated's input
function reverseKeys (obj) {
return Object.fromEntries(
Object.entries(obj)
.reverse()
.map(
([k, v]) => {
if (v.constructor === Object && v.constructor !== Array && v !== null) {
return [k, reverseKeys(v)]
} else if (v.constructor === Array && v !== null) {
const a = []
v.forEach(elem => {
if ((elem.constructor === Object || elem.constructor === Array) && elem !== null) {
a.push(reverseKeys(elem))
} else {
a.push(elem)
}
})
return [k, a]
} else {
return [k, v]
}
}
)
)
}
You can map over Object.entries recursively.
let o = {
a: 'val1',
b: 'val2',
c: {
ca: 'val3',
cb: 'val4'
},
d: [{a : 1, b : 2}, {c: 3}]
};
function reverseKeys(obj) {
if (Array.isArray(obj)) return [...obj].reverse().map(reverseKeys);
if (Object.getPrototypeOf(obj) === Object.prototype)
return Object.fromEntries(Object.entries(obj).reverse()
.map(([k, v]) => [k, reverseKeys(v)]));
return obj;
}
console.log(reverseKeys(o));
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.
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; }
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))
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;
}
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.
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]));
}
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);
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));
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
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: {} }
// 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));
//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.
I'm working on a project where I get an object in input like this one :
const obj = {
a: 'somestring',
b: 42,
c: {
d: 'foo',
e: 'bar'
},
f: [1, 2]
};
and I need to create some variables to get to this output :
const a = "somestring"
const b = 42
const c.d = "foo"
const c.e = "bar"
const f[0] = 1
const f[1] = 2
I got a result with this code :
for (const [k1, v1] of Object.entries(obj)) {
if (typeof v1 === "object") {
if (Array.isArray(v1)) {
for (const [k2, v2] of Object.entries(v1)) {
console.log(`const ${k1}[${k2}] = ${v2}`);
}
} else {
for (const [k2, v2] of Object.entries(v1)) {
console.log(`const ${k1}.${k2} = ${v2}`);
}
}
} else {
console.log(`const ${k1} = ${v1}`);
}
}
But when I get an object more complex like this one :
const obj = {
a: [
{
b: 'lorem'
},
{
c: 'ipsum'
}
],
d: {
e: {
f : 'foobar'
}
}
};
My output look like this :
const a[0] = [object Object]
const a[1] = [object Object]
const d.e = [object Object]
I can't find any relevant solutions. Is there a solution or npm package for this?
There is a feature called destructuring docs click here
This feature as the docs say will help you create new variables from a nested object.
Taking a object which is nested you can access its leaf values and assign them directly to vars like this:
const o = {
a: 'a',
b: {
c : 'c',
d: {
e: 'e'
}
}
};
const {a ,b : { c, d: {e} }} = o;
alert(a);
alert(c);
alert(e);
Edit: it works with arrays as well not objects only
You can do it with eval function in JS:
const obj = {
a: 'somestring',
b: 42,
c: {
d: 'foo',
e: 'bar'
},
f: [1, 2]
};
var log = console.log;
for (let key in obj) {
//log(key);
eval(`var ${key} = obj.${key}`);
}
log(a);
log(b);
log(c);
log(f);
I think you want something like this:
const obj = {
a: 'somestring',
b: 42,
c: {
d: 'foo',
e: 'bar'
},
f: [1, 2]
};
const parse = (obj, prefix = '', isArray=false) => {
Object.entries(obj).forEach(([k, v]) => {
if (typeof v === 'object') parse(v, `${k}`, Array.isArray(v))
else {
const value = (typeof v === 'string') ? `"${v}"` : v;
const before = isArray ? '[' : prefix ? '.' : '';
const after = isArray ? ']' : '';
console.log(`const ${prefix}${before}${k}${after} = ${value}`)
}
});
}
parse(obj);
Given input:
[{ a: 1 }, { b: 2 }, { c: 3 }]
How to return:
{ a: 1, b: 2, c: 3 }
For arrays it's not a problem with lodash but here we have array of objects.
Use Object.assign:
let merged = Object.assign(...arr); // ES6 (2015) syntax
var merged = Object.assign.apply(Object, arr); // ES5 syntax
Note that Object.assign is not yet implemented in many environment and you might need to polyfill it (either with core-js, another polyfill or using the polyfill on MDN).
You mentioned lodash, so it's worth pointing out it comes with a _.assign function for this purpose that does the same thing:
var merged = _.assign.apply(_, [{ a: 1 }, { b: 2 }, { c: 3 }]);
But I really recommend the new standard library way.
With lodash, you can use merge():
var arr = [ { a: 1 }, { b: 2 }, { c: 3 } ];
_.merge.apply(null, [{}].concat(arr));
// → { a: 1, b: 2, c: 3 }
If you're doing this in several places, you can make merge() a little more elegant by using partial() and spread():
var merge = _.spread(_.partial(_.merge, {}));
merge(arr);
// → { a: 1, b: 2, c: 3 }
Here is a version not using ES6 methods...
var arr = [{ a: 1 }, { b: 2 }, { c: 3 }];
var obj = {};
for(var i = 0; i < arr.length; i++) {
var o = arr[i];
for(var key in o) {
if(typeof o[key] != 'function'){
obj[key] = o[key];
}
}
}
console.log(obj);
fiddle: http://jsfiddle.net/yaw3wbb8/
You can use underscore.extend function like that:
var _ = require('underscore');
var a = [{ a: 1 }, { b: 2 }, { c: 3 }];
var result = _.extend.apply(null, a);
console.log(result); // { a: 1, b: 2, c: 3 }
console.log(a); // [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]
And to prevent modifying original array you should use
var _ = require('underscore');
var a = [{ a: 1 }, { b: 2 }, { c: 3 }];
var result = _.extend.apply(null, [{}].concat(a));
console.log(result); // { a: 1, b: 2, c: 3 }
console.log(a); // [ { a: 1 }, { b: 2 }, { c: 3 } ]
Here can test it
Adding to the accepted answer, a running code snippet with ES6.
let input = [{ a: 1 }, { b: 2 }, { c: 3 }]
//Get input object list with spread operator
console.log(...input)
//Get all elements in one object
console.log(Object.assign(...input))
I've got a neat little solution not requiring a polyfill.
var arr = [{ a: 1 }, { b: 2 }, { c: 3 }];
var object = {};
arr.map(function(obj){
var prop = Object.getOwnPropertyNames(obj);
object[prop] = obj[prop];
});
Hope that helps :)
Here is a nice usage of Object.assign with the array.prototype.reduce function:
let merged = arrOfObjs.reduce((accum, val) => {
Object.assign(accum, val);
return accum;
}, {})
This approach does not mutate the input array of objects, which could help you avoid difficult to troubleshoot problems.
With more modern spread operator
arrOfObj.reduce( (acc, curr) => ({ ...acc, ...cur }) );
You can easily flat your object to array.
function flatten(elements) {
return elements.reduce((result, current) => {
return result.concat(Array.isArray(current) ? flatten(current) : current);
}, []);
};
6 years after this question was asked.
Object.assign is the answer (above) I like the most.
but is this also legal ?
let res = {};
[{ a: 1 }, { b: 2 }, { c: 3 }].forEach(val => {
let key = Object.keys(val);
console.log(key[0]);
res[key] = val[key];
})
const data = [
[{ a: "a" }, { b: "b" }, { c: "c" }],
[{ d: "d" }, { e: "e" }, { f: "f" }],
[{ g: "g" }, { h: "h" }, { i: "i" }],
];
function convertToObject(array){
const response = {};
for (let i = 0; i < array.length; i++) {
const innerArray = array[i];
for (let i = 0; i < innerArray.length; i++) {
const object = innerArray[i];
const keys = Object.keys(object);
for (let j = 0; j < keys.length; j++) {
const key = keys[j];
response[key] = object[key];
}
}
}
return response;
}
console.log(convertToObject(data));
function carParts(manufacturer, model, ...parts) {
return { manufacturer, model, ...Object.assign(...parts) };
}
console.log(
carParts(
"Honda",
"2008",
{ color: "Halogen Lights" },
{ Gears: "Automatic Gears" },
{ LED: "Android LED" },
{ LED: "Android LED1" }
)
);
This is how i have done.
I have an array like so:
var array = [
{
name: "a",
value: 1
},
{
name: "a",
value: 2
},
{
name: "a",
value: 3
},
{
name: "b",
value: 0
},
{
name: "b",
value: 1
}
];
And I need an array like this:
var newarray = [
{
name: "a",
value: 2
},
{
name: "b",
value: 0.5
}
]
Where the new array has each unique name as an object with the average value.
Is there an easy way to accomplish this?
You'll have to loop through the array, computing the sum and counts for each object. Here's a quick implementation:
function average(arr) {
var sums = {}, counts = {}, results = [], name;
for (var i = 0; i < arr.length; i++) {
name = arr[i].name;
if (!(name in sums)) {
sums[name] = 0;
counts[name] = 0;
}
sums[name] += arr[i].value;
counts[name]++;
}
for(name in sums) {
results.push({ name: name, value: sums[name] / counts[name] });
}
return results;
}
Demonstration
Note, this kind of thing can be made much easier if you use a library like Underscore.js:
var averages = _.chain(array)
.groupBy('name')
.map(function(g, k) {
return {
name: k,
value: _.chain(g)
.pluck('value')
.reduce(function(x, y) { return x + y })
.value() / g.length
};
})
.value();
Demonstration
var array = [
{
name: "a",
value: 1
},
{
name: "a",
value: 2
},
{
name: "a",
value: 3
},
{
name: "b",
value: 0
},
{
name: "b",
value: 1
}
];
var sum = {};
for(var i = 0; i < array.length; i++) {
var ele = array[i];
if (!sum[ele.name]) {
sum[ele.name] = {};
sum[ele.name]["sum"] = 0;
sum[ele.name]["count"] = 0;
}
sum[ele.name]["sum"] += ele.value;
sum[ele.name]["count"]++;
}
var result = [];
for (var name in sum) {
result.push({name: name, value: sum[name]["sum"] / sum[name]["count"]});
}
console.log(result);
You can do it with Alasql library with one line of code:
var newArray = alasql('SELECT name, AVG([value]) AS [value] FROM ? GROUP BY name',
[array]);
Here I put "value" in square brackets, because VALUE is a keyword in SQL.
Try this example at jsFiddle
Here is a ES2015 version, using reduce
let arr = [
{ a: 1, b: 1 },
{ a: 2, b: 3 },
{ a: 6, b: 4 },
{ a: 2, b: 1 },
{ a: 8, b: 2 },
{ a: 0, b: 2 },
{ a: 4, b: 3 }
]
arr.reduce((a, b, index, self) => {
const keys = Object.keys(a)
let c = {}
keys.map((key) => {
c[key] = a[key] + b[key]
if (index + 1 === self.length) {
c[key] = c[key] / self.length
}
})
return c
})
And a possible solution using ECMA5 (as we seem to be missing one)
var sums = {},
averages = Object.keys(array.reduce(function (previous, element) {
if (previous.hasOwnProperty(element.name)) {
previous[element.name].value += element.value;
previous[element.name].count += 1;
} else {
previous[element.name] = {
value: element.value,
count: 1
};
}
return previous;
}, sums)).map(function (name) {
return {
name: name,
average: this[name].value / this[name].count
};
}, sums);
On jsFiddle
October 2020, I think this is the shortest way (ES6+)
const getAveragesByGroup = (arr, key, val) => {
const average = (a, b, i, self) => a + b[val] / self.length;
return Object.values(
arr.reduce((acc, elem, i, self) => (
(acc[elem[key]] = acc[elem[key]] || {
[key]: elem[key],
[val]: self.filter((x) => x[key] === elem[key]).reduce(average, 0),
}),acc),{})
);
};
console.log(getAveragesByGroup(array, 'name', 'value'))
Try by yourself :)