Here are two objects:
const obj1 = {a: null, b: "b"}
const obj2 = {a: "a", b: null}
How can I merge the two objects and get the following object?
{a: "a", b: "b"}
I can do this:
const merged = {...obj1, ...obj2}
But it returns this:
{ a: "a", b: null }
Is there a way to merge two objects while prefering not null (nor empty, undefined, etc.) values?
function merge(obj1, obj2) {
answer = {}
for(key in obj1) {
if(answer[key] === undefined || answer[key] === null)
answer[key] = obj1[key];
}
for(key in obj2) {
if(answer[key] === undefined || answer[key] === null)
answer[key] = obj2[key];
}
return answer
}
I suggest using Lodash mergeWith for this:
const obj1 = {a: null, b: "b"}
const obj2 = {a: "a", b: null}
const result = _.mergeWith({}, obj1, obj2,
(a, b) => b === null ? a : undefined
)
// result: {a: "a", b: "b"}
Try this.
const obj1 = {a: null, b: "b"} const obj2 = {a: "a", b: null} const obj3 = {} for (var k in obj1) { obj3[k] = obj1[k] ? obj1[k] : obj2[k]; } console.log(obj3);
What about a simple forEach()
loop on object key
. It will work for both null
and undefined
values:
const obj1 = {a: null, b: "b"}; const obj2 = {a: "a", b: null}; const merged = {}; Object.keys(obj1).forEach((key) => merged[[key]] = obj1[key] ? obj1[key] : obj2[key]); console.log(merged);
I figured I could accomplish this by combining these libraries.
" @ramda/mergedeepwith " is ported from ramda .
" flat " is Flatten/unflatten nested Javascript objects.
yarn add @ramda/mergedeepwith flat ramda
This is workaround.
import { default as flat } from "flat"; import mergeDeepWith from "@ramda/mergedeepwith"; import * as R from "ramda"; const obj1 = { a: null, b: "bbb", c: { "c-1": "cccc", }, d: [ { "d-1": "ddd-1", }, { "d-2": null, }, { "d-3": [0, 1, "a", 100], // omit 0 }, ], }; const obj2 = { a: "aaa", b: null, c: { "c-1": null, }, d: [ { "d-1": null, }, { "d-2": "ddd-2", }, { "d-3": ["b", "c"], }, ], }; const flattenedObj1 = flat.flatten(obj1); const flattenedObj2 = flat.flatten(obj2); const mergedObj = R.mergeDeepWith( (x, y) => { if (x) return x; if (y) return y; return null; }, flattenedObj2, flattenedObj1 ); console.log(JSON.stringify(flat.unflatten(mergedObj), null, 2));
Output is here.
$ node index.js { "a": "aaa", "b": "bbb", "c": { "c-1": "cccc" }, "d": [ { "d-1": "ddd-1" }, { "d-2": "ddd-2" }, { "d-3": [ "b", "c", "a", 100 ] } ] }
function omitNull (obj) {
const keys = Object.keys(obj);
const _obj = {};
for (const key of keys) {
const value = obj[key];
if (value !== null) {
_obj[key] = value;
}
}
return _obj;
}
const merged = {
...omitNull(obj1),
...omitNull(obj2)
};
result = {};
for(key in obj1){
result[key] = obj1[key] === null ? obj2[key] : obj1[key];
}
this merges two objects in one loop
You could make a function that gets an array of objects as a parameter.
This way, no matter how many objects you have, you will get the result of them merged together excluding the undefined and null values. Just send them as an array.
There you pass all your objects, map them, then iterate through their key,values with for (const [key, value] of Object.entries(obj))
and exclude the ones that are undefined
or null
See below
const obj1 = { a: null, b: "goodb", c: 0, } const obj2 = { a: "gooda", b: null, c: undefined } function cleanObjects(arr) { let o = {} arr.map((obj) => { for (const [key, value] of Object.entries(obj)) { typeof value === 'undefined' || value === null ? delete obj[key] : o[key] = value; } }) return o; } const result = cleanObjects([obj1, obj2]) console.log(result)
Here's a modified version of the accepted answer:
function merge(obj1, obj2) {
let merged = { ...obj1 }
for (key in obj2) {
if (merged[key] === undefined || merged[key] === null)
merged[key] = obj2[key];
}
return merged
}
You can extend the Javascript ObjectConstructor itself. Add a function merge
, This overrides the next non-null value and adds all the objects' properties to one single object.
/* @/utils.js */
Object.merge = function (...objs) {
const obj = {};
objs.reduce((prevObj, currentObj) => {
if (typeof prevObj === 'object' && typeof currentObj === 'object') Object.entries(currentObj).forEach(([k, v]) => {
obj[k] = v === null || v === undefined
? prevObj[k]
: v;
});
return obj;
}, {});
return obj;
};
/* @/app.js */
Object.merge(a,b,c,...z);
Object.merge = function (...objs) { const obj = {}; objs.reduce((prevObj, currentObj) => { if (typeof prevObj === 'object' && typeof currentObj === 'object') Object.entries(currentObj).forEach(([k, v]) => { obj[k] = v === null || v === undefined ? prevObj[k] : v; }); return obj; }, {}); return obj; } const john = { name: 'John Doe', age: 40, heigth: '5.3ft' } const jane = { name: 'Jane Doe', age: null, heigth: '4.1ft' } const mark = { name: 'Mark', age: 35, heigth: null } const ghost = { name: null, age: null, heigth: null, planet: 'unknown' } const noname = { name: null, age: 100, heigth: '100ft', planet: '100M-E' } console.log(Object.merge(john,jane,mark,ghost,noname))
function merge(obj1, obj2) { if (!obj1 && !obj2) return null if (!obj1) return obj2; if (!obj2) return obj1; result = {} const keys = [...new Set([...Object.keys(obj1), ...Object.keys(obj2)])]; keys.forEach(key => { result[key] = obj1[key] || obj2[key] }) return result } console.log(merge(null, undefined)) console.log(merge({ a: 1}, null)) console.log(merge(null, { b : 2})) console.log(merge(null, { })) console.log(merge({a: 1, b : null, c : undefined, d: 'd'}, { b : 2, c: 5, d: null}))
const obj1 = { a: null, b: "goodb", c: 0, d: 133, f: null } const obj2 = { a: "gooda", b: null, e: 1, c: undefined } function cleanObjects(arr) { let o = {} arr.map((obj) => { for (const [key, value] of Object.entries(obj)) { typeof value === 'undefined' || value === null? delete obj[key]: o[key] = value; } }) return o; } const result = cleanObjects([obj1, obj2]) console.log(result)
For completeness sake, someone should mention the Nullish coalescing operator
So for top-level properties you can choose the not-nullish one:
const obj1 = { a: null, b: "b" }
const obj2 = { a: "a", b: null }
const obj3 = { foo: Infinity }
let merge = (...objects) =>
objects
.reduce((result, next) => ({ ...result, ...Object.entries(next)
.reduce((resultingEntries, [key, value]) => ({ ...resultingEntries, [key]: value ?? result[key] }), {})}))
merge(obj1, obj2, obj3)
// {a: "a", b: "b", foo: "c"}
If you also want to copy deeper properties, things start to get REALLY ugly:
let merge2 = (...objects) =>
objects
.reduce((result, next) => ({ ...result, ...Object.entries(next)
.reduce((resultingEntries, [key, value]) => ({ ...resultingEntries, [key]:
result && result[key] && typeof result[key] === 'object' // <- null unsafe here
? merge3(...objects.map(o => o && o[key]))
: value ?? result[key] // <- to here?
}), {})}))
Perhaps better to extract it to a function
const getProps = (key, ...objects) => objects.filter(isntNullOrUndefined).map(o => o[key]).filter(isntNullOrUndefined)
const isntNullOrUndefined = x => x !== null && x !== undefined
const merge2 = (...objects) =>
objects
.filter(isntNullOrUndefined)
.reduce((acc, obj) => ({
...acc,
...Object
.keys(obj)
.filter(key => !(key in acc))
.filter(key => isntNullOrUndefined(obj[key]))
.reduce((acc2, key) => ({
...acc2,
[key]: typeof obj[key] === 'object'
? merge2(...getProps(key, ...objects))
: getProps(key, ...objects)[0]
}), {})
}), {})
The next step is to do it with iterators, which might be slicker than reduce
using the ??
operator.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.