153

I am looking for the best way to "add" multiple JavaScript objects (associative arrays).

For example, given:

a = { "one" : 1, "two" : 2 };
b = { "three" : 3 };
c = { "four" : 4, "five" : 5 };

what is the best way to compute:

{ "one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Vlad
  • 9,180
  • 5
  • 48
  • 67
  • 3
    A semantic note: despite the [] syntax, they aren't arrays at all. Order is not really guaranteed. – Álvaro González Mar 16 '10 at 12:44
  • 2
    Iteration order is not guaranteed per ecma standards. But it is the way it is implemented in most browsers. (From John Resig) This behavior is explicitly left undefined by the ECMAScript specification. In ECMA-262, section 12.6.4: The mechanics of enumerating the properties ... is implementation dependent. However, specification is quite different from implementation. All modern implementations of ECMAScript iterate through object properties in the order in which they were defined. Because of this the Chrome team has deemed this to be a bug and will be fixing it. – Ruan Mendes Mar 16 '10 at 13:44

14 Answers14

223

ECMAscript 6 introduced Object.assign() to achieve this natively in Javascript.

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

MDN documentation on Object.assign()

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

Object.assign is supported in many modern browsers but not yet all of them. Use a transpiler like Babel and Traceur to generate backwards-compatible ES5 JavaScript.

adiga
  • 34,372
  • 9
  • 61
  • 83
filoxo
  • 8,132
  • 3
  • 33
  • 38
  • 6
    This is one of the best I could say. Since E6 is now mostly used, Object.assign is the best answer. – Vimalraj Selvam Nov 22 '16 at 10:53
  • 9
    If you don't know how many objects need to be merge, bevause they are in an array you can merge them as follows: `Object.assign.apply({}, [{a: 1}, {b: 2}, ....])` – Jeanluca Scaljeri Feb 10 '18 at 12:21
  • 3
    An alternative to using `Object.assign.apply` to merge an array of objects, is to instead use the spread operator: `Object.assign( ...objects )` – Spen Aug 04 '18 at 15:25
  • @Spen that is already included as an answer to this question. I don't seen a benefit to duplicating it here. – filoxo Aug 06 '18 at 15:53
  • after 4 hours, this answer solved my problem in Angular 7. Thanks for the simplicity and accurateness of answer. – Gel Sep 14 '19 at 18:38
  • What an answer! – suchislife Aug 31 '21 at 15:33
67

ECMAScript 6 has spread syntax. And now you can do this:

const obj1 = { 1: 11, 2: 22 };
const obj2 = { 3: 33, 4: 44 };
const obj3 = { ...obj1, ...obj2 };

console.log(obj3); // {1: 11, 2: 22, 3: 33, 4: 44}
adiga
  • 34,372
  • 9
  • 61
  • 83
valex
  • 5,163
  • 2
  • 33
  • 40
  • 3
    This should be the accepted answer, because it is the most modern one. `Object.assign` is old now and not as readable as this one. – rugk Nov 27 '20 at 11:02
  • yep, but what if you have an array of objects. with assign you can just do ```Object.assign({}, ...objcs)``` or ```Object.assign.apply({}, objcs)``` – Hexception Aug 16 '21 at 18:01
35

This should do it:

function collect() {
  var ret = {};
  var len = arguments.length;
  for (var i = 0; i < len; i++) {
    for (p in arguments[i]) {
      if (arguments[i].hasOwnProperty(p)) {
        ret[p] = arguments[i][p];
      }
    }
  }
  return ret;
}

let a = { "one" : 1, "two" : 2 };
let b = { "three" : 3 };
let c = { "four" : 4, "five" : 5 };

let d = collect(a, b, c);
console.log(d);

Output:

{
  "one": 1,
  "two": 2,
  "three": 3,
  "four": 4,
  "five": 5
}
adiga
  • 34,372
  • 9
  • 61
  • 83
cletus
  • 616,129
  • 168
  • 910
  • 942
  • Doesn't `length` lookup the size of the array at each invocation? I'm so used to writing `for (var i = 0, len = array.length; i < len; ++i)` that I can't remember off the top of my head why I started doing it. – tvanfosson Mar 16 '10 at 12:47
  • 1
    Yes, that's correct. It is better performance to cache the length once. However, because the size of the arguments "array" is not likely to ever be very large, it won't really matter in this case. – jhurshman Mar 16 '10 at 13:07
  • 1
    No, except slightly on IE6. Accessing the length property costs the same as accessing the len variable. – Alsciende Mar 16 '10 at 13:08
  • @Alsciende, I believe that is incorrect. For large arrays, you should always cache length when iterating. @cletus Why are you using hasOwnProperty, prototype user? – Ruan Mendes Mar 16 '10 at 13:49
  • 1
    @Juan I believe you are incorrect, and I did some tests to make up my mind. Caching the length is an easy myth of optimization that's been obsolete for many years, and it makes the code (slightly) less readable. Actually, caching the length sometimes slows down the browser (Safari). – Alsciende Mar 16 '10 at 14:15
  • @Alsciende It is true that I hadn't run any length caching tests in a while, and in doing so, I did find that caching the length is not significantly faster for newer browsers, webkit, firefox and IE8. However, I code for the corporate world that is infested with IE6. So I'll keep caching my length. – Ruan Mendes Mar 22 '10 at 16:44
  • 2
    Wouldn't it be better to write 'for (var p in...' instead of 'for (p in...' ? – Hugo Dec 12 '11 at 08:55
  • You shouldn't write `for (var i=0; i – Gerd Wagner Jul 01 '14 at 10:48
34

You could use jquery's $.extend like this:

let a = { "one" : 1, "two" : 2 },
    b = { "three" : 3 },
    c = { "four" : 4, "five" : 5 };

let d = $.extend({}, a, b, c)

console.log(d)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
adiga
  • 34,372
  • 9
  • 61
  • 83
12

Underscore has few methods to do this;

1. _.extend(destination, *sources)

Copy all of the properties in the source objects over to the destination object, and return the destination object.

_.extend(a, _.extend(b, c));
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

Or

_.extend(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.extend(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

2. _.defaults(object, *defaults)

Fill in undefined properties in object with values from the defaults objects, and return the object.

_.defaults(a, _.defaults(b, c));
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

Or

_.defaults(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.defaults(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }
gihanchanuka
  • 4,783
  • 2
  • 32
  • 32
8

Shallow-cloning (excluding prototype) or merging of objects is now possible using a shorter syntax than Object.assign().

Spread syntax for object literals was introduced in ECMAScript 2018):

const a = { "one": 1, "two": 2 };
const b = { "three": 3 };
const c = { "four": 4, "five": 5 };

const result = {...a, ...b, ...c};
// Object { "one": 1, "two": 2 , "three": 3, "four": 4, "five": 5 }

Spread (...) operator is supported in many modern browsers but not all of them.

So, it is recommend to use a transpiler like Babel to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.

This is the equivalent code Babel will generate for you:

"use strict";

var _extends = Object.assign || function(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i];
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
};

var a = { "one": 1, "two": 2 };
var b = { "three": 3 };
var c = { "four": 4, "five": 5 };

var result = _extends({}, a, b, c);
// Object { "one": 1, "two": 2 , "three": 3, "four": 4, "five": 5 }
Thiago Santos
  • 333
  • 3
  • 6
5

To merge a dynamic number of objects, we can use Object.assign with spread syntax.

const mergeObjs = (...objs) => Object.assign({}, ...objs);

The above function accepts any number of objects, merging all of their properties into a new object with properties from later objects overwriting those from previous objects.

Demo:

const mergeObjs = (...objs) => Object.assign({}, ...objs);
const a = {prop: 1, prop2: '2'},
      b = {prop3: 3, prop4: [1,2,3,4]}
      c = {prop5: 5},
      d = {prop6: true, prop7: -1},
      e = {prop1: 2};
const abcd = mergeObjs(a,b,c,d);
console.log("Merged a,b,c,d:", abcd);
const abd = mergeObjs(a,b,d);
console.log("Merged a,b,d:", abd);
const ae = mergeObjs(a,e);//prop1 from e will overwrite prop1 from a
console.log("Merged a,e:", ae);

To merge an array of objects, a similar method may be applied.

const mergeArrayOfObjs = arr => Object.assign({}, ...arr);

Demo:

const mergeArrayOfObjs = arr => Object.assign({}, ...arr);
const arr = [
  {a: 1, b: 2},
  {c:1, d:3},
  {abcd: [1,2,3,4], d: 4}
];
const merged = mergeArrayOfObjs(arr);
console.log(merged);
Unmitigated
  • 76,500
  • 11
  • 62
  • 80
4

Why should the function be restricted to 3 arguments? Also, check for hasOwnProperty.

function Collect() {
    var o={};
    for(var i=0;i<arguments.length;i++) {
      var arg=arguments[i];
      if(typeof arg != "object") continue;
      for(var p in arg) {
        if(arg.hasOwnProperty(p)) o[p] = arg[p];
      }
    }
    return o;
}
Alsciende
  • 26,583
  • 9
  • 51
  • 67
4

It's easy using ES7 spread operator for an object, in your browser console put

({ name: "Alex", ...(true  ? { age: 19 } : { })}) // {name: "Alex", age: 19}
({ name: "Alex", ...(false ? { age: 19 } : { })}) // {name: "Alex",        }
Purkhalo Alex
  • 3,309
  • 29
  • 27
3
function Collect(a, b, c) {
    for (property in b)
        a[property] = b[property];

    for (property in c)
        a[property] = c[property];

    return a;
}

Notice: Existing properties in previous objects will be overwritten.

Björn
  • 29,019
  • 9
  • 65
  • 81
3

ES6 ++

The question is adding various different objects into one.

let obj = {};
const obj1 = { foo: 'bar' };
const obj2 = { bar: 'foo' };
Object.assign(obj, obj1, obj2);
//output => {foo: 'bar', bar: 'foo'};

lets say you have one object with multiple keys that are objects:

let obj = {
  foo: { bar: 'foo' },
  bar: { foo: 'bar' }
}

this was the solution I found (still have to foreach :/)

let objAll = {};

Object.values(obj).forEach(o => {
  objAll = {...objAll, ...o};
});

By doing this we can dynamically add ALL object keys into one.

// Output => { bar: 'foo', foo: 'bar' }
Rip3rs
  • 1,284
  • 12
  • 21
1

Probably, the fastest, efficient and more generic way is this (you can merge any number of objects and even copy to the first one ->assign):

function object_merge(){
    for (var i=1; i<arguments.length; i++)
       for (var a in arguments[i])
         arguments[0][a] = arguments[i][a];
   return arguments[0];
}

It also allows you to modify the first object as it passed by reference. If you don't want this but want to have a completely new object containing all properties, then you can pass {} as the first argument.

var object1={a:1,b:2};
var object2={c:3,d:4};
var object3={d:5,e:6};
var combined_object=object_merge(object1,object2,object3); 

combined_object and object1 both contain the properties of object1,object2,object3.

var object1={a:1,b:2};
var object2={c:3,d:4};
var object3={d:5,e:6};
var combined_object=object_merge({},object1,object2,object3); 

In this case, the combined_object contains the properties of object1,object2,object3 but object1 is not modified.

Check here: https://jsfiddle.net/ppwovxey/1/

Note: JavaScript objects are passed by reference.

Selay
  • 6,024
  • 2
  • 27
  • 23
1

Simplest: spread operators

var obj1 = {a: 1}
var obj2 = {b: 2}
var concat = { ...obj1, ...obj2 } // { a: 1, b: 2 }
Rodolfo Paranhos
  • 793
  • 2
  • 7
  • 18
-2
function collect(a, b, c){
    var d = {};

    for(p in a){
        d[p] = a[p];
    }
    for(p in b){
        d[p] = b[p];
    }
    for(p in c){
        d[p] = c[p];
    }

    return d;
}
Álvaro González
  • 142,137
  • 41
  • 261
  • 360