In this tutorial, let’s look at the different ways to concat two objects in JavaScript.
We’re going to look at using both pre-defined and manual methods to merge our objects.
Use the spread operator to merge objects
Let’s say we have two objects, obj1 and obj2.
With a string, you would have used concatenation to combine the two strings to produce a new string.
Similarly, does obj1 + obj2 work to combine two objects? Unfortunately, it doesn’t. You’ll end up with an error if you try this method.
What if we create a new object and place our two child objects inside it, separated by commas, like this:
let personInfo = { name: 'John', age: 16, height: '170cms' }; let schoolInfo = { id: 101, gpa: 3.8 }; let fullDetails = { personInfo, schoolInfo };
Unfortunately, this doesn’t work either.
Let’s print this new object and see.
console.log(fullDetails); /* { "personInfo": { "name": "John", "age": 16, "height": "170cms" }, "schoolInfo": { "id": 101, "gpa": 3.8 } }*/ console.log(fullDetails.personInfo.name); // John console.log(fullDetails.name); // undefined
Look at the output displayed above. With merging, we expect the properties of the two objects to be a part of the new object. Instead, we just get two whole objects stuffed into a new variable, and the properties are not directly accessible (if we try, we get ‘undefined’).
So, we need a way to separate the properties from the objects, so they can be copied one by one, or we’ll end up with references to the entire objects copied to our new object, and that doesn’t count as a proper merge.
That’s where the spread operator helps us. Just add three dots before the object name, and you’re good to go.
This operator, as the name implies, will spread the objects’ properties, hence individually copying them into the new object, as shown below:
let fullDetails = { ... personInfo, ... schoolInfo }
Now, print the object’s name again, and you’ll get an object that has the properties of both the objects readily available to access and manipulate, as shown below:
console.log(fullDetails); // {name: 'John', age: 16, height: '170cms', id: 101, gpa: 3.8} console.log(fullDetails.name); //John
Handling concatenating/merging objects with the same properties
There’s an issue with the method we looked at in the previous section though. If both your objects have the same properties, while merging them with the spread operator, the common property values will be overwritten.
The value of the object in the right side of the merge will override the value of the object in the left.
Look at the example below:
let personInfo = { name: 'John', age: 16, height: '170cms' }; let schoolInfo = { id: 101, gpa: 3.8, age: 17 }; let fullDetails = { ... personInfo, ... schoolInfo } console.log(fullDetails); //{name: 'John', age: 17, height: '170cms', id: 101, gpa: 3.8}
As you can see, age is now 17 after the merge because schooInfo had age as 17. Since schoolInfo was on the right side of the merge, fullDetails inherited the schoolInfo object’s property values first, and personInfo’s age value was overwritten.
If personInfo was in the right side of the merge, age would have been 16, as you can see below:
let fullDetails = { ... schoolInfo, ... personInfo } console.log(fullDetails); //{name: 'John', age: 16, height: '170cms', id: 101, gpa: 3.8}
So, use this method for objects whose properties aren’t the same.
If they are, at least make sure that overwriting the values won’t affect your final result.
In the above example, assuming that schools have the latest information, re-writing the age to the one in schoolInfo wouldn’t negatively affect our data, so we can continue using the spread operator for the merge.
Merging nested objects
What if one of the objects you’re trying to merge is a nested object, as in, what if it has yet another object inside it?
Look at the example below:
let personInfo = { name: 'John', age: 16, appearance: { height: '170cms', weight: '70kgs' } };
The personInfo object has a property called appearance, which is an object in itself. Now, try to perform the merge on this new object and look at the result.
console.log(fullDetails); // {name: 'John', age: 17, appearance: {…}, id: 101, gpa: 3.8}
As you can see, the merge happened on all fronts, including the inner object. But things aren’t as good as they appear.
This inner object wasn’t merged or copied property-wise to our new object. We’ve just created a reference to the personInfo.appearance property in the fullDetails object. Look at the comparison below:
console.log(personInfo.appearance === fullDetails.appearance); //true
As you can see the appearance object in personInfo and fullDetails objects are the same. That might not seem different to you, since looking at them from the outside, they’re both objects and their values are the same, but that’s not how objects work.
Even if two objects have the same properties and values, on comparison, they’ll never be considered the same. Look at the below example for proof:
let personInfo = { name: 'John', age: 16, appearance: { height: '170cms', weight: '70kgs' } }; let fullDetails = { name: 'John', age: 16, appearance: { height: '170cms', weight: '70kgs' } } console.log(personInfo.appearance === fullDetails.appearance); //false
Technically, both personInfo and fullDetails have the same structure and values but they still return false on comparison.
But when we merged personInfo with schoolInfo earlier, the fact that we got back ‘true’ while comparing the ‘appearance’ inner object on both personInfo and fullDetails says that the ‘appearance’ object and its properties were never created separately on fullDetails.
Instead, the browser just created a reference to the ‘apperance’ object in personInfo from fullDetails.
If you want to do a proper merge for inner objects as well, you need to look into more thorough options, and we’ll be discussing those further down the tutorial.
Using Object.assign
You can also use the Object.assign method to do a merge. This method was originally created to ‘create’ a new object, but it can also be modified a little to merge our objects to an empty object, as shown below:
let fullDetails = Object.assign({}, personInfo, schoolInfo);
Shallow merge vs. Deep merge
In a previous example, we noticed that, when an object has another object inside it, that object doesn’t get properly merged to our new object, but instead, we just create a reference to the original object. That’s what a shallow merge does; it’s just a level one merge.
On the other hand, a deep merge would properly copy every single property, including the properties of the inner objects.
The best way to do a deep merge on 2 or more objects is by using the lodash module and its .merge method. This method was created for the express purpose of deep merging two or more objects. Let’s look at how it works now.
Deep merge using the lodash _.merge
Lodash isn’t a built-in JavaScript module, so you need to import it first. We’re using the CDN version in the
section of our index.html file. Make sure you load the CDN first before you create a link to the program’s main JavaScript file, so when you use the module, it’s properly imported beforehand.<!DOCTYPE html> <html lang="en"> <head> <title>JavaScript practice</title> <link rel="stylesheet" type="text/css" href="style.css"> <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script> <script src="lodashmod.js"></script> </head> <body> </body> </html>
In the lodashmod.js file we created, let’s create our objects.
Then, all we have to do is use the .merge method of the ‘-‘, which refers to everything in the ‘lodash’ module.
This method requires 3 arguments for our example, the first being an empty object to do the merge in, and the last 2 arguments being the 2 objects being merged.
As you can see below, the merge was successful, and there’s no reference to the ‘appearance’ property in the personInfo object from the ‘appearance’ property in fullDetails object.
let personInfo = { name: 'John', age: 16, appearance: { height: '170cms', weight: '70kgs' } }; let schoolInfo = { id: 101, gpa: 3.8, age: 17 }; let fullDetails = _.merge({}, personInfo, schoolInfo); console.log(fullDetails); //{name: 'John', age: 17, appearance: {…}, id: 101, gpa: 3.8} console.log(personInfo.apperance === fullDetails.appearance); //false
Using a for in loop
You can also use a ‘for in’ loop to do the merge. The steps are:
1. Create an empty object first.
2. Then use the ‘for in’ loop on the personInfo object first, where for every iteration of the loop, you’ll get access to one of the properties. You can then create the same property in fullDetails and assign the value from personInfo.
3. Next, create another ‘for in’ loop and follow the same process.
Now, you’ve successfully merged the 2 objects.
let personInfo = { name: 'John', age: 16 }; let schoolInfo = { id: 101, gpa: 3.8 }; let fullDetails = {}; for(const prop in personInfo) { fullDetails[prop] = personInfo[prop]; } for(const prop in schoolInfo) { fullDetails[prop] = schoolInfo[prop]; } console.log(fullDetails); // {name: 'John', age: 16, id: 101, gpa: 3.8}
This method does not work with a deep merge though, for which you’re better off using the .merge method of the ‘lodash’ module.
Using forEach and Object.keys
The Object.keys method returns an array of all the property names in an object, as you can see below:
console.log(Object.keys(personInfo)); // ['name', 'age']
Now, you can use the forEach method on this array, and for every iteration, you can create a new property in the fullDetails object with the extracted ‘key’ (in the returned array), and assign it the corresponding key value in the personInfo and schoolInfo objects respectively.
You need two forEach loops to complete the process for this example, as you can see below:
let fullDetails = {}; Object.keys(personInfo).forEach(key => { fullDetails[key] = personInfo[key]; }); Object.keys(schoolInfo).forEach(key => { fullDetails[key] = schoolInfo[key]; });
Using the reduce method
You can also use the ‘reduce’ method to arrive at the same merged result. The ‘reduce’ method is used to find the aggregate of all the elements in an array.
The steps to merge two objects using the ‘reduce’ method are as follows:
1. So, we can create an array with the object names ‘personInfo’ and ‘schoolInfo’ as the elements and apply the ‘reduce’ method on that.
2. For every iteration, it’ll access one of the objects. At that point, use the Object.keys method to extract the property names in an array.
3. Apply the ‘reduce’ method again to this array, and for every iteration, apply the property’s value to the newly formed ‘temp’ variable.
4. In the ‘reduce’ method, you get 2 temporary variables, one that holds the aggregate (or the result of the entire operation) and the other that holds the temporary value of that iteration alone.
5. Return the aggregate (which is in ‘temp’) for the inner ‘reduce’ method, and finally, return the ‘sum’ value, which is the aggregate for the outer ‘reduce’ method, which contains the merged object, as you can see below:
const fullDetails = [personInfo, schoolInfo].reduce((sum, obj) => { return Object.keys(obj).reduce((temp, key) => { temp[key] = obj[key]; return temp; }, sum); }, {}); console.log(fullDetails); // {name: 'John', age: 16, id: 101, gpa: 3.8}
That’s it! We’ve looked at various ways to concat or merge two objects in JavaScript, including doing both shallow and deep merges using pre-defined JavaScript methods, external JavaScript modules and manual methods using loops.