Merging JavaScript Objects
Michael McShinsky

Merging JavaScript Objects

A short guide to creating a new object from multiple objects.

June 10, 2020

This article will show you how to merge two or more JavaScript objects into a new object. The new object will contain the properties of all the objects that were merged together. JavaScript provides multiple ways to accomplish this task with a lot of different methods. Here, we will take a look at three of the more popular approaches. These are:

  • Spread - The spread (…) operator
  • Assign - The Object.assign() method
  • Loop - Object loop method (for…in)

Spread Operator

The spread operator was introduced with ES6 and allows us to merge multiple arguments or objects and returns a new combined value anywhere defined in your code.

let obj1 = { firstName: 'John', lastName: 'Doe', email: 'johnydoe1@domain.com' };

let obj2 = { email: 'john.doe@domain.com', phone: '012–345–6789' };

let obj3 = { city: 'Salt Lake City', state: 'Utah' }

let newObj = {…obj1, …obj2, …obj3};

// {
//  firstName: 'John',
//  lastName: 'Doe',
//  email: 'john.doe@email.com',
//  phone: '012–345–6789',
//  city: 'Salt Lake City',
//  state: 'Utah'
// }

If any of the objects or values added to the spread operation share the same property or name (e.g. firstName), the last one passed into your operation will be the value taken and overwrites any earlier values with the same key when creating your new object. This can be seen in our example above.

One important factor in using this approach is that the shape of your final value is defined by the wrapping characters. That is, we use the opening and closing braces ("{ }") to determine the resulting shape of our action, in this case, an object. Another result shape we can use are brackets ("[ ]") when combining arrays to create a new array.

Object.assign()

The object prototype method assign is a tried and true method introduced with ES5. The spread operator has since taken over to do the same thing, but if you don’t have browser support for the latest and greatest, or you have to support older browsers, the assign method may be your go to in these niche situations.

let obj1 = { firstName: 'John', lastName: 'Doe', email: 'johnydoe1@domain.com' };

let obj2 = { email: 'john.doe@domain.com', phone: '012–345–6789' };

let obj3 = { city: 'Salt Lake City', state: 'Utah' }

let newObj = Object.assign({}, obj1, obj2, obj3);

// {
//  firstName: 'John',
//  lastName: 'Doe',
//  email: 'john.doe@email.com',
//  phone: '012–345–6789',
//  city: 'Salt Lake City',
//  state: 'Utah'
// }

In the code shown above, the assign method takes one or many arguments just like the spread operator. Just like the spread operator, if we need to define and default or ending shape of our variable, we add in an empty object {} that the following objects will fill into.

Object Loops (for…in)

Our final method for merging objects are loops. This is an older method and is not nearly as popular as spread and assign, but it does come with some convenient customizations if you need more granular control over how your objects will be merged. We’ll upgrade it slightly to use some ES6 flavoring (for…in, and reduce) while we’re at it. No need to stay old school in everything, right?

let obj1 = { firstName: 'John', lastName: 'Doe', email: 'johnydoe1@domain.com' };

let obj2 = { email: 'john.doe@domain.com', phone: '012–345–6789' };

let obj3 = { city: 'Salt Lake City', state: 'Utah' }

function merge(…arr) {
  return arr.reduce((acc, cur) => {
    // Can add custom logic for how keys or nested values should be handled.
    for (let key in cur) {
      acc[key] = cur[key];
    }
    return acc;
  }, {});
}

let newObj = merge(obj1, obj2, obj3);

// {
//  firstName: 'John',
//  lastName: 'Doe',
//  email: 'john.doe@email.com',
//  phone: '012–345–6789',
//  city: 'Salt Lake City',
//  state: 'Utah'
// }

We can see in the code above that we first loop over the passed in object(s). In this case we are making use of the rest parameter syntax in our function parameters. Then we loop over each object and it’s keys in order to create our final object.

One important note to remember is that when JavaScript merges your objects and values into a new object, this is considered a shallow copy. This means that top level values contain no reference to the old objects values, whereas deeper or nested values may contain a reference to the original object. If you update our newly created object, old object references may update as well!

There are some verbose examples of what are called “deep copying” functions provided by different libraries and individuals (e.g. jQuery.extend(), lodash, underscore, JSON.parse(JSON.stringify), etc…). Be warned that these are expensive operations. Doing deep copies over large lists of data could highly impact user experience and performance on the device running your code! If you find yourself doing this a lot or wanting to do this a lot, take a step back and think about the architecture of your code and decide if there is a more DRY or maintainable way to break apart and manage your code’s state.

Final Thoughts

Whichever is best for your project is the best approach for you! Which browsers you need support will be one of the main factors in the choice you make between these and other solutions. If you are only supporting the latest and greatest browsers, you will probably end up using the spread operator a lot more than the others. On the other hand, if you have to want that granularity or old browser support, you may fall back to manual loops. Sometimes, it’s about what your team decides to use to be consistent with each other. In the end, it’s all up to you!