Luigi Cavalieri - Full-stack Developer

A Custom Function to Deeply Merge Two Objects in JavaScript

A reusable solution to deeply merge two JavaScript objects by leveraging the 'Array.prototype.reduce()' function, Recursion and the Spread Syntax.

A Custom Function to Deeply Merge Two Objects in JavaScript

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.

1/**
2 * In the event you happen to rename this function,
3 * don't forget to rename the recursive call too!
4 */
5const mergeObjectsDeep = ( baseObj, incomingObj ) => (
6  Object.keys( incomingObj ).reduce( ( mergedObj, key ) => {
7    if ( isObject( baseObj[key] ) && isObject( incomingObj[key] ) ) {
8      return {
9        ...mergedObj,
10        [key]: mergeObjectsDeep( baseObj[key], incomingObj[key] )
11      };
12    }
13
14    return {
15      ...mergedObj,
16      [key]: incomingObj[key]
17    };
18  }, baseObj )
19);

You might have noticed two calls to the custom function isObject(), well, as its signature suggests it is a mono-task function whose body can be disclosed as follows:

1const isObject = ( variable ) => {
2  return (
3      typeof variable === 'object' &&
4      !Array.isArray( variable ) &&
5      variable !== null
6  );
7};

A Brief Explanation and A Mandatory Detail

The code works like this:

  • When the two values ​​being compared are not 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.