Deeply Merging Two Objects in JS6+

While working on a recent JavaScript project, I found myself in need of merging together an object of default values, with a second object of custom values. The complication was that the two objects could contain other objects nested in, and that the operation could not limit itself to shallowly merge those child objects. After a little work, mergeObjectsDeep() was born to be shared.

The Proposed Solution

The function below is the final result of mixing together three main ingredients: the Array.prototype.reduce() function, Recursion and the Spread Syntax.

/**
 * In the event you happen to rename the function,
 * don't forget to rename the recursive call too.
 */
const mergeObjectsDeep = ( baseObj, incomingObj ) => (
  Object.keys( incomingObj ).reduce( ( mergedObj, key ) => {
    if ( typeof baseObj[key] === 'object' && typeof incomingObj[key] === 'object' ) {
      return {
        ...mergedObj,
        [key]: mergeObjectsDeep( baseObj[key], incomingObj[key] )
      };
    }

    return {
      ...mergedObj,
      [key]: incomingObj[key]
    };
  }, baseObj )
);

A Brief Explanation and A Mandatory Detail

The code works like this:

  • When the two values ​​being compared aren't both objects, we use the spread syntax to replace the value in baseObj with the corresponding value in incomingObj, if the key under evaluation exists in both of them, otherwise the value of incomingObj under evaluation is "pushed" into baseObj as a new key-value pair.
  • When, on the other hand, the two values ​​being compared are both objects, the recursion steps into work jointly with the spread syntax to deeply merge the two child objects.

I want to draw your attention on the Array.prototype.reduce() function, here used to compose the final, merged object returned by mergeObjectsDeep().

Have you noticed what the initial value of reduce() is?

Passing baseObj to the reduce() function as the initial value from which to start constructing the final, merged object, is the little trick in the absence of which mergeObjectsDeep() would not return the expected result.

New to the Spread Syntax?

So, I strongly encourage you to peruse the "Spread in object literals" section of this carefully written article on the MDN Web Docs site.