Merge Multiple Nested Objects in JavaScript

04-15-2020


If you have a need to merge multiple simple or complex JavaScript objects together into a single object, overriding or extending previous values, this helper function is handy.

I often use multiple different objects to manage things like the configuration values for my applications. I might have one object for local development, one for a dev environment, prod, and so on. The problem with this is that I end up with a lot of duplicate values across these different configurations, where the differences are really minor. I wanted a way to “merge” all of the values together into a single object, either overriding or extending values of one object with values from a subsequent object.

Here’s a example, I have two objects:

1
2
const objA = { name: "brian", port: 3000 };
const objB = { port: 5000 };

That I want to merge into a single object:

result

1
const result = { name: "brian", port: 5000 }

Object.assign() can easily handle this, no problem. The issue with using something like Object.assign() comes in when we have complex objects with nesting, arrays, etc. The solution becomes hairy very quickly. This is the solution I came up with instead.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// To Use: mergeDeep({a:1,b:{c:3}}, {a:2}) -> {a:2, b:{c:3}}
// const config = mergeDeep(config1, config2) For use in config setup

exports.mergeDeep = (...objects) => {
const isObject = obj => obj && typeof obj === 'object';

return objects.reduce((prev, obj) => {
Object.keys(obj).forEach((key) => {
const prevVal = prev[key];
const origVal = obj[key];
if (Array.isArray(prevVal) && Array.isArray(origVal)) {
prev[key] = prevVal.concat(...origVal);
} else if (isObject(prevVal) && isObject(origVal)) {
prev[key] = mergeDeep(prevVal, origVal);
} else {
prev[key] = origVal;
}
})
return prev;
}, {});
}

This helper function, which I export as a function called “mergeDeep”, parses through the …objects passed into the function looping over each object to determine if it contains more nesting or arrays and starts to build a new object with the results. The function will recursively call itself until it finds the furthest child node and stops. The resulting object contains all of the information across all objects that were supplied, overriding or extending values as necessary.