This question already has answers here:
How to deep merge instead of shallow merge?
(47 answers)
Closed 3 years ago.
So I want to prevent overriding shared keys, when doing a mixin with JS, so we have:
const v = {
a: {
b: {
c: 4,
d: 'str'
}
}
};
console.log(Object.assign({}, v, {a: {b: {c: 5}}}));
this will log:
{ a: { b: { c: 5 } } }
but I am looking for this instead:
{ a: { b: { c: 5, d: 'str' } } }
anyone know how to do this (preferably without a library).
A fairly naïve solution which cannot handle cycles:
const mixin = (a, b) => {
for (let [key, val] of Object.entries(b)) {
if (typeof val !== 'object') {
a[key] = b[key];
continue;
}
if (val === null) {
a[key] = b[key];
continue;
}
if (!a.hasOwnProperty(key)) {
a[key] = b[key];
continue;
}
mixin(a[key], b[key]);
}
return a;
};
const mixinInclusive = (...v) => {
return v.reduce(mixin, {});
};
console.log(mixinInclusive(v, {a: {b: {c: 5}}}));
Related
This question already has answers here:
How can I merge properties of two JavaScript objects dynamically?
(69 answers)
Closed 9 months ago.
I have an 3 nested array of Objects and I need to merge them all in single array of objects by comparing key. and if any object have same key then their inner object will merge. And if key is not same then also the nested array of objects will merge*
var obj1 = {
a: {
c: 3
}
}
var obj2 = {
b: {
e: 40
}
}
var obj3 = {
d: {
x: 30
},
a: {
f: 66
}
}
// The expected output will be like this -
//
/*OUTPUT:::
{
a: {
c: 3,
f: 66
},
b: {
e: 40
},
d: {
x: 30
}
}
Please tell me which approach would be suitable ?
You can group the objects using Array.prototype.reduce.
const
obj1 = { a: { c: 3 } },
obj2 = { b: { e: 40 } },
obj3 = { d: { x: 30 }, a: { f: 66 } };
const res = [obj1, obj2, obj3].reduce((r, o) => {
Object.entries(o).forEach(([k, v]) => {
r[k] = { ...r[k], ...v };
});
return r;
}, {});
console.log(res);
This question already has answers here:
One liner to flatten nested object
(19 answers)
Closed 1 year ago.
I am trying to achieve it Like a:1,b:2:,c:3,e:4,g:5,h:6
But not getting success.
Facing error this. But is the best way to do it.
const input = {
a: 1,
b: 2,
c: 3,
d: {
e: 4,
f: {
g: 5,
h: 6
}
}
}
const getValue = (values) => {
for (let i in Object.keys(values)) {
if (Object.keys(values[Object.keys(values)[i]]).length > 0) {
console.log('v', Object.keys(values)[i])
getValue(Object.keys(values)[i])
} else {
// console.log(Object.keys(values)[i],Object.values(values)[i])
}
}
}
getValue(input)
You can iterate through each key of object and for object value recursively call your getValue() function.
const input = { a:1, b:2, c:3, d:{ e:4, f:{ g:5, h:6 } } }
const getValue = (values) => {
for (const key of Object.keys(values)) {
if(typeof values[key] === 'object' && values[key] !== null) {
getValue(values[key]);
} else {
console.log(`${key}: ${values[key]}`);
}
}
}
getValue(input);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use recursion to get the desired result.
const input = {
a: 1,
b: 2,
c: 3,
d: {
e: 4,
f: {
g: 5,
h: 6,
},
},
};
const result = {};
function getValues(obj) {
for (let key in obj) {
if (typeof obj[key] !== `object`) result[key] = obj[key];
else getValues(obj[key]);
}
}
getValues(input);
console.log(result);
Edited : you can do something like this
const input = {a:1,b:2,c:3,d:{e:4,f:{g:5,h:6 }}}
Object.assign({}, ...function _flatten(o) { return [].concat(...Object.keys(o).map(k => typeof o[k] === 'object' ? _flatten(o[k]) : ({[k]: o[k]})))}(input))
//{a: 1, b: 2, c: 3, e: 4, g: 5, …}
you can see more details here
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);
I'm trying to create an updated object from an existing object.
The sample object is:
// sample object
const testObj = {
a: 1,
b: {
c: 2,
d: {
e: 3,
f: {
g: 4
}
}
}
};
I want to create a new object from the above object with some concatenation of each value:
// expected object
const expectedObject= {
a: '1 a',
b: {
c: '2 a',
d: {
e: '3 a',
f: {
g: '4 a'
}
}
}
};
here is my sample code:
let expectedObject = {};
const newObject = object => {
Object.entries(object).forEach(([key, value]) => {
if (typeof value === "object") {
Object.keys(value).map(key => {
value[key] = value[key] + " a";
return value;
});
expectedObject[key] = value;
//return newTest;
} else {
expectedObject[key] = value;
return expectedObject;
}
});
return expectedObject;
};
console.log(newObject(testObj));
the outcome in console is:
{a: 1, b: {…}}
a: 1
b:
c: "2 a"
d: "[object Object] a"
__proto__: Object
__proto__: Object
I wanted to use recursion here and also tried it but no luck.
any help, thanks?
You could get a new object my mapping changed values and creating new objects.
function map(object, fn) {
return Object.fromEntries(Object
.entries(object)
.map(([k, v]) => [k, v && typeof v === 'object' ? map(v, fn) : fn(v)])
);
}
var object = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4 } } } },
result = map(object, v => v + ' a');
console.log(result);
If you have arrays inside, you could add a check in advance and map the values.
const
map = fn => {
const iter = v => v && typeof v === 'object'
? Array.isArray(v)
? v.map(iter)
: Object.fromEntries(Object.entries(v).map(([k, v]) => [k, iter(v, fn)]))
: fn(v);
return iter;
};
var object = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4, k: [5, 6] } } } },
addA = map(v => v + ' a'),
result = addA(object);
console.log(result);
This is simply a refactoring of the answer from #user633183. I like that approach a lot, but think it can be simplified by extracting two more reusable functions. This started as a comment on that answer, but I thought it would be better to be explicit.
const map = (f) => (a) =>
a.map(f)
const mapObj = (f) => (o) =>
Object .entries (o) .reduce ( (a, [k, v] ) => ({ ...a, [k]: f(v) }), {})
const traverse = (f) => (t) =>
Array.isArray(t)
? map (traverse (f)) (t)
: Object(t) === t
? mapObj (traverse (f)) (t)
: f (t)
const input =
{ a: [ 1, 11, 111 ], b: { c: 2, d: { e: [ 3, { f: { g: 4 } } ] } } }
const output =
traverse(x => `${x} a`) (input)
console.log(output)
mapObj can be written in many different ways. Here are two alternatives:
const mapObj = (f = identity) => (o = {}) =>
Object .fromEntries (Object .entries (o) .map (([ k, v ]) => [ k, f (v) ]))
const mapObj = (f = identity) => (o = {}) =>
Object .assign .apply (null, Object .entries (o) .map (([ k, v ]) => ({ [k]: f (v)
Here's an approach using a modification of the original code to demonstrate what needed to be changed in order to make it work. You had some things switched up reading the value and setting the new one. Also I'm using the spread operator to clone the object before modifying it.
const testObj = {
a: 1,
b: {
c: 2,
d: {
e: 3,
f: {
g: 4
}
}
}
};
const newObject = object => {
const clonedObj = { ...object };
const entries = Object.entries(clonedObj);
entries.forEach(([key, value]) => {
if (typeof value === "object") {
clonedObj[key] = newObject(value);
} else {
clonedObj[key] = value + " a";
}
});
return clonedObj;
};
console.log(newObject(testObj));
console.log(testObj); // prove that the original object hasn't changed
Here's a simple recursive technique. It is similar to Nina's but it preserves arrays, if present in the structure.
If the input, t, is an array, create a new array by traversing each array value, v, with the traversing function, f
(inductive) Otherwise t is not an array. If t is an object, create a new object from key value pairs, [ k, v ], by traversing each value, v, with the traversing function, f
(inductive) Otherwise t is not an array and t is not an object. This means t is either a primitive value, such as string, number, or null
Numbered comments below correspond to the explanation above -
const identity = x =>
x
const traverse = (f = identity, t = {}) =>
Array.isArray(t) // 1
? Array.from(t, v => traverse(f, v))
: Object(t) === t // 2
? Object.fromEntries(Object.entries(t).map(([ k, v ]) => [ k, traverse(f, v) ]))
: f (t) // 3
const input =
{ a: [ 1, 11, 111 ], b: { c: 2, d: { e: [ 3, { f: { g: 4 } } ] } } }
const output =
traverse(x => `${x} a`, input)
console.log(output)
Here is a solution using object-scan. It works by building the solution at the same time as the input is traversed.
// const objectScan = require('object-scan');
const testObj = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4 } } } };
const cloneAndModify = (obj) => objectScan(['**'], {
breakFn: ({ property, value, isLeaf, context }) => {
if (property === undefined) {
return;
}
const ref = context[context.length - 1];
if (!(property in ref)) {
ref[property] = isLeaf ? `${value} a` : {};
}
context.push(ref[property]);
},
filterFn: ({ context }) => {
context.pop();
}
})(obj, [{}])[0];
const r = cloneAndModify(testObj);
console.log(r);
// => { b: { d: { f: { g: '4 a' }, e: '3 a' }, c: '2 a' }, a: '1 a' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan
I have a function called nestedEvenSum that takes an object as a parameter and loops through the object trying to find values that are even numbers. When it finds an even number, it adds that to the variable sum. Here is my code:
function nestedEvenSum (obj) {
let sum = 0;
function inner(obj) {
for (let i in obj) {
if (typeof obj[i] === "number" && obj[i] % 2 === 0) sum += obj[i];
if (typeof obj[i] === 'object') return inner(obj[i]);
}
}
inner(obj);
return sum;
}
I give my function the following object:
var obj2 = {
a: 2,
b: {b: 2, bb: {b: 3, bb: {b: 2}}},
c: {c: {c: 2}, cc: 'ball', ccc: 5},
d: 1,
e: {e: {e: 2}, ee: 'car'}
};
and my loop keeps returning 6 instead of 10. For some reason, the for loop is exiting after the recursive inner() calls unwind for the second key (b). I'm not sure why the for loop isn't continuing on with the three remaining keys. If someone could point me in the right direction, I'd appreciate it. Thanks!
Don't return inside of your loop. That is going to exit your recursion. Instead you need to CALL the function again which is not the same as returning it.
function nestedEvenSum (obj) {
let sum = 0;
function inner(obj) {
for (let i in obj) {
if (typeof obj[i] === "number" && obj[i] % 2 === 0) sum += obj[i];
if (typeof obj[i] === 'object') inner(obj[i]);
}
}
inner(obj);
return sum;
}
var obj2 = {
a: 2,
b: {b: 2, bb: {b: 3, bb: {b: 2}}},
c: {c: {c: 2}, cc: 'ball', ccc: 5},
d: 1,
e: {e: {e: 2}, ee: 'car'}
};
console.log(nestedEvenSum(obj2));
Beside the false return statement for objects, you could take a single function and add the returned result of the function for nested objects.
function nestedEvenSum(obj) {
let sum = 0;
for (let i in obj) {
if (typeof obj[i] === "number" && obj[i] % 2 === 0) sum += obj[i];
if (obj[i] && typeof obj[i] === 'object') sum += nestedEvenSum(obj[i]);
}
return sum;
}
var obj2 = { a: 2, b: { b: 2, bb: { b: 3, bb: { b: 2 } } }, c: { c: { c: 2 }, cc: 'ball', ccc: 5 }, d: 1, e: { e: { e: 2 }, ee: 'car' } };
console.log(nestedEvenSum(obj2));