3

I have two Object like this and want to merge them:

const obj1 = {
  1: { foo: 1 },
  2: { bar: 2, fooBar: 3 },
  3: { fooBar: 3 },
};

const obj2 = {
  1: { foo: 1, bar: 2 },
  2: { bar: 2 },
};

const merged = someMergingMethod(obj1, obj2);

merged === {
  1: { foo: 1, bar: 2 },
  2: { bar: 2, fooBar: 3 },
  3: { fooBar: 3 },
};

I mean, I want to not only merge the objects, but also merge their object values' properties too if the key is duplicated. Because just merged = { ...obj1, ...obj2 }; overwrites the properties with obj2's.

What is the easiest way to do this?

I can use ES2017 and other libraries like lodash.

Taichi
  • 2,297
  • 6
  • 25
  • 47
  • 1
    Possible duplicate of [How can I merge properties of two JavaScript objects dynamically?](https://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically) –  Jan 26 '19 at 07:58
  • 1
    No, because that question does not consider merging the properties too. – Taichi Jan 26 '19 at 08:00
  • What's supposed to happen if `obj1.2.bar` is 2 but `obj2.2.bar` is 3? Anyway, it should be closed as "too broad" then, because SO isn't a free code writing service. –  Jan 26 '19 at 08:02
  • Possible duplicate of https://stackoverflow.com/questions/50176456/merging-two-javascript-objects-into-one – Naor Tedgi Jan 26 '19 at 08:23
  • I'm sorry for that :( – Taichi Jan 28 '19 at 02:39
  • Closely related to [this other question](https://stackoverflow.com/questions/73419719/merge-nested-objects-in-javascript-and-append-matching-properties-in-array/73420289), but there they are merging matching properties in an array. – Rodrigo Rodrigues Aug 20 '22 at 08:08

6 Answers6

6

You can use spread operator.

Update :

if obj2 has some properties that obj1 does not have?

Initially i wrote this answer assuming the keys are indexed like 0,1 and so on but as you mentioned in comment this is not the case than you can build a array of keys and than iterate over it as

as very concisely added in comment by @Nick [...Object.keys(obj1), ...Object.keys(obj2)]

let obj1 = {1: { foo: 1 },2: { bar: 2, fooBar: 3 },3: { fooBar: 3 },};
let obj2 = {1: { foo: 1, bar: 2 },2: { bar: 2 },};

let keys = [...new Set([...Object.keys(obj1),...Object.keys(obj2)])]
let  op = {}
let merged = keys.forEach(key=>{
  op[key] = {
    ...obj1[key],
    ...obj2[key]
  }
})
console.log(op)
Code Maniac
  • 37,143
  • 5
  • 39
  • 60
  • Thanks, but what if `obj2` has some properties that `obj1` does not have? :( – Taichi Jan 26 '19 at 08:02
  • 3
    @Taichi Not the original writer of the answer but I think by changing `Objects.keys(obj1).forEach` to `[...Object.keys(obj1), ...Object.keys(obj2)].forEach` should do the trick – Nick Parsons Jan 26 '19 at 08:08
  • 1
    @NickParsons this is so neat, i was writing a function to achieve this. cheers mate :) – Code Maniac Jan 26 '19 at 08:14
  • 2
    @CodeManiac no worries, just another thought, maybe by settings keys equal to `[...new Set([...Object.keys(obj1),...Object.keys(obj2)])];` would be better to remove duplicates... Also, I think `merged` should be `merged = keys.forEach()` ;) – Nick Parsons Jan 26 '19 at 08:17
1

Since you mentioned you can use lodash you can use merge like so:

_.merge(obj1, obj2)

to get your desired result.

See working example below:

const a = {
  1: { foo: 1 },
  2: { bar: 2, fooBar: 3 },
  3: { fooBar: 3 },
},

b = {
  1: { foo: 1, bar: 2 },
  2: { bar: 2 },
  4: {foo: 1}
},

res = _.merge(a, b);
console.log(res);
<script src="https://cdn.jsdelivr.net/lodash/4.16.4/lodash.min.js"></script>
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
1

I have exactly what you want. This function will traverse through each nested object and combine it with the other. I've only tested it with 5 nested levels down the tree but, theoretically, it should work for any number of nested objects as it is a recursive function.

//this function is similar to object.assign but,
//leaves the keys which are common among both bojects untouched
function merge(object1, object2)
{
    function combine(p, q)
    {
        for(i in q)
            if(!p.hasOwnProperty(i))
                p[i]= q[i];
        return p;
    }
    obj1= Object.assign(combine(obj1, obj2), obj1);//for the first level 
    obj1= Object.assign(traverse(obj1, obj2), obj1);//for subsequent levels down theobjectt tree

//this function traverses each nested boject and combines it with the other object
    function traverse(a, b)
    {
        if(typeof(a) === "object" && typeof(b) === "object")
            for(i in b)
                if(typeof(a[i]) === "object" && typeof(b[i]) === "object")
                    a[i]= Object.assign(traverse(a[i], b[i]), a[i]);
                else
                    Object.assign(combine(a, b), a);
        return a;
    }
    return obj1;
}

console.log(merge(obj1, obj2));

Here is a working example of a much more complex object merging

var obj1 = {
  1: { foo: 1 },
  2: { bar: 2, fooBar: 3 },
  3: { fooBar: 3, boor:{foob: 1, foof: 8} },
  4: {continent: {
    asia: {country: {india: {capital: "delhi"}, china: {capital: "beiging"}}},
    europe:{country:{germany: {capital: "berlin"},france: {capital: "paris"}}}
   },
   vegtables: {cucumber: 2, carrot: 3, radish: 7}
  }
};

var obj2 = {
  1: { foo: 1, bar: 2 },
  2: { bar: 2 },
  3: {fooBar: 3, boor:{foob: 1, boof: 6}, boob: 9 },
  4: {continent: {
    northamerica: {country: {mexico: {capital: "mexico city"}, canada: {capital: "ottawa"}},},
    asia: {country: {Afghanistan : {capital: "Kabul"}}}
   }
  },
  5: {barf: 42}
};

//this function is similar to object.assign but,
//leaves the keys which are common among both bojects untouched
function merge(object1, object2)
{
 function combine(p, q)
 {
  for(i in q)
   if(!p.hasOwnProperty(i))
    p[i]= q[i];
  return p;
 }
 obj1= Object.assign(combine(obj1, obj2), obj1);//for the first level 
 obj1= Object.assign(traverse(obj1, obj2), obj1);//for subsequent levels down the object tree

//this function traverses each nested boject and combines it with the other object
 function traverse(a, b)
 {
  if(typeof(a) === "object" && typeof(b) === "object")
   for(i in b)
    if(typeof(a[i]) === "object" && typeof(b[i]) === "object")
     a[i]= Object.assign(traverse(a[i], b[i]), a[i]);
    else
      Object.assign(combine(a, b), a);
  return a;
 }
 return obj1;
}

console.log(merge(obj1, obj2));
0

you can use Object.assign and and assign object properties to empty object.

var a =  {books: 2};
var b = {notebooks: 1};
var c = Object.assign( {}, a, b );
console.log(c);

or

You could use merge method from Lodash library. can check here : https://www.npmjs.com/package/lodash

Rohit Jaiswal
  • 257
  • 4
  • 6
0

Are you looking like this?

we can use this way to merge two objects.

const person = { name: 'David Walsh', gender: 'Male' };
const attributes = { handsomeness: 'Extreme', hair: 'Brown', eyes: 'Blue' };
const summary = {...person, ...attributes};

/*

Object {
  "eyes": "Blue",
  "gender": "Male",
  "hair": "Brown",
  "handsomeness": "Extreme",
  "name": "David Walsh",
}

*/

0

This can be easily accomplished with a combination of the neat javascript spread syntax, Array and Object prototype functions, and destructuring patterns.

[obj1,obj2].flatMap(Object.entries).reduce((o,[k,v])=>({...o,[k]:{...o[k],...v}}),{})

As simple as this!


For a very detailed explanation of how this works, refer to this extended answer to a similar question.

Rodrigo Rodrigues
  • 7,545
  • 1
  • 24
  • 36