152

What’s the best/standard way of merging two associative arrays in JavaScript? Does everyone just do it by rolling their own for loop?

3

16 Answers 16

210

With jQuery, you can call $.extend:

var obj1 = {a: 1, b: 2};
var obj2 = {a: 4, c: 110};

var obj3 = $.extend(obj1, obj2);

obj1 == obj3 == {a: 4, b: 2, c: 110} // Pseudo JavaScript

(associative arrays are objects in JavaScript)

Look here: http://api.jquery.com/jQuery.extend/


Like rymo suggested, it's better to do it this way:

obj3 = $.extend({}, obj1, obj2);
obj3 == {a: 4, b: 2, c: 110}

As here obj1 (and obj2) remain unchanged.


In 2018, the way to do it is via Object.assign:

var obj3 = Object.assign({}, obj1, obj2);
obj3 === {a: 4, b: 2, c: 110} // Pseudo JavaScript

If working with ES6, this can be achieved with the spread operator:

const obj3 = { ...obj1, ...obj2 };
Sign up to request clarification or add additional context in comments.

4 Comments

or to avoid mucking obj1, use an empty object as target: $.extend({}, obj1, obj2)
Yes, when dealing with closures and objects passed by reference, take rymo's advice to avoid affecting the original object for subsequent operations.
For modern browsers, check the solution by @manfred using Object.assign(), which doesn't rely on JQuery if you're not using the library already.
It works, but it alters the first parameter and that's something you need to be aware of. See developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
68

Now in 2016 I would say the best/standard way is Object.assign().

In pure JavaScript, without any jQuery is needed.

obj1 = {a: 1, b: 2};
obj2 = {a: 4, c: 110};
obj3 = Object.assign({},obj1, obj2);  // Object {a: 4, b: 2, c: 110}

More information, examples and polyfill on Object.assign().

1 Comment

What is the type for obj3? what if obj1 is type IABC, will obj3 keep that type afterwards?
49

This is how Prototype does it:

Object.extend = function(destination, source) {
    for (var property in source) {
        if (source.hasOwnProperty(property)) {
            destination[property] = source[property];
        }
    }
    return destination;
};

It is called as, for example:

var arr1 = { robert: "bobby", john: "jack" };
var arr2 = { elizabeth: "liz", jennifer: "jen" };

var shortnames = Object.extend(arr1, arr2);

1 Comment

You'll need the test source.hasOwnProperty(property) to make sure you only copy the immediate properties over. As it is, this could will copy all properties including those derived from Object.prototype
21

Keep it simple...

function mergeArray(array1,array2) {
  for(item in array1) {
    array2[item] = array1[item];
  }
  return array2;
}

2 Comments

You to check hasOwnProperty to avoid copying any properties on the arrau1.prototype
@LeeGee is right. You need that hasOwnProperty check. Referring to the objects as arrays is also misleading (they're objects), the arg names should convey that array2 is mutated or a new object should be returned. I would edit the answer but in effect it would become almost exactly Jonathan Fingland's edited answer which has already been corrected.
14

Underscore.js also has an extend method:

Copy all of the properties in the source objects over to the destination object. It's in-order, so the last source will override properties of the same name in previous arguments.

_.extend(destination, *sources)

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}

Comments

7

In dojo, the 2-objects/arrays "merge" would be lang.mixin(destination, source) -- you can also mix multiple sources into one destination, etc -- see the mixin function's reference for details.

Comments

6

Rolling Your Own Extend/Mixin Function

function extend(objects) {
    var args
        , first = Array.prototype.slice.call(arguments, 0, 1)[0]
        , second;

    if (arguments.length > 1) {
        second = Array.prototype.splice.call(arguments, 1, 1)[0];
        for (var key in second) {
            first[key] = second[key];
        }
        args = Array.prototype.slice.call(arguments, 0);
        return extend.apply(this, args);
    }

    return first;
}

...

var briansDirections = {
    step1: 'Remove pastry from wrapper.',
    step2: 'Place pastry toaster.',
    step3: 'Remove pastry from toaster and enjoy.',
};
extend(briansDirections, { step1: 'Toast Poptarts' }, { step2: 'Go ahead, toast \'em' }, { step3: 'Hey, are you sill reading this???' });

...

This simply extends a splat of objects, recursively. Also, note that this recursive function is TCO (Tail-Call Optimized) as its return is the last call to itself.

Additionally, you may want targeted properties. In this case, you may want to condense objects based upon id, quantity, or another property. This approach could have a small book written about it and requires object-juxtaposition and can get very complex. I've written a small library for this which is available upon request.

Hope this helps!

Comments

6

Do you want to overwrite a property if the names are the same, but the values are not?

And do you want to permanently change one of the original objects, or do you want a new merged object returned?

function mergedObject(obj1, obj2, force){
    for(var p in obj1) 
        this[p] = obj1[p];
    for(var p in obj2){
        if(obj2.hasOwnProperty(p)){
            if(force || this[p] === undefined) 
                this[p] = obj2[p];
            else{
                n = 2;
                while(this[p+n] !== undefined)
                    ++n;
                this[p+n] = obj2[p];
            }
        }
    }
}

Comments

4

In 2019 you have 2 good options:

Object assigning [doc]

const result = Object.assign({}, baseObject, updatingObject);

Object spreading [doc]

const result = { ...baseObject, ...updatingObject};

The first one tends to be safer, more standard and polyvalent. A good pros and cons here

Comments

4
  1. In JavaScript, there isn't any notion of associative arrays. There are objects.
  2. The only way to merge two objects is to loop for their properties and copy pointers to their values that are not primitive types and values for primitive types to another instance

1 Comment

Objects in Javascript are implemented as associative arrays, so there certainly is the notion of same
1

Yahoo UI (YUI) also has a helper function for this:

YAHOO.namespace('example');

YAHOO.example.set1 = { foo : "foo" };
YAHOO.example.set2 = { foo : "BAR", bar : "bar" };
YAHOO.example.set3 = { foo : "FOO", baz : "BAZ" };

var Ye = YAHOO.example;

var merged = YAHOO.lang.merge(Ye.set1, Ye.set2, Ye.set3);

Comments

1

jQuery has a Boolean thingamajiggy for deep copy. You could do something like this:

MergeRecursive = function(arr1, arr2) {
    $.extend(true, arr1, arr2);
    return arr1;
};

Also, you can edit this function to support n-arrays to merge.

ArrayMergeRecursive = function() {
     if(arguments.length < 2) {
          throw new Error("ArrayMergeRecursive: Please enter two or more objects to merge!");
     }

    var arr1 = arguments[0];
    for(var i=0; i<=arguments.length; i++) {
        $.extend(true, arr1, arguments[i]);
    }

    return arr1;
};

So now you can do

var arr1 = {'color': {'mycolor': 'red'}, 3: 5},
    arr2 = {4: 10, 'color': {'favorite': 'green', 0: 'blue'}},
    arr3 = ['Peter', 'Jhon', 'Demosthenes'],
    results = ArrayMergeRecursive(arr1, arr2, arr3); // (arr1, arr2 ... arrN)
console.log("Result is:", results);

Comments

0

I needed a deep-object-merging. So all of the other answers didn't help me very much. _.extend and jQuery.extend do well, unless you have a recursive array like i do. But it ain't so bad, you can program it in five minutes:

var deep_merge = function (arr1, arr2) {
    jQuery.each(arr2, function (index, element) {
        if (typeof arr1[index] === "object" && typeof element === "object") {
            arr1[index] = deep_merge(arr1[index], element);
        } else if (typeof arr1[index] === "array" && typeof element === "array") {
            arr1[index] = arr1[index].concat(element);
        } else {
            arr1[index] = element;
        }
    });
    return arr1;
}

Comments

0

To merge arrays in jQuery what about $.merge?

var merged = $.merge([{id:3, value:'foo3'}], [{id:1, value:'foo1'}, {id:2, value:'foo2'}]);
merged[0].id == 3;
merged[0].value == 'foo3';
merged[1].id == 1;
merged[1].value == 'foo1';
merged[2].id == 2;
merged[2].value == 'foo2';

Comments

0

Recursive solution (extends also arrays of objects) + null checked

var addProps = function (original, props) {
    if(!props) {
        return original;
    }
    if (Array.isArray(original)) {
        original.map(function (e) {
            return addProps(e, props)
        });
        return original;
    }
    if (!original) {
        original = {};
    }
    for (var property in props) {
        if (props.hasOwnProperty(property)) {
            original[property] = props[property];
        }
    }
    return original;
};

Tests

console.log(addProps([{a: 2}, {z: 'ciao'}], {timestamp: 13}));
console.log(addProps({single: true}, {timestamp: 13}));
console.log(addProps({}, {timestamp: 13}));
console.log(addProps(null, {timestamp: 13}));

[ { a: 2, timestamp: 13 }, { z: 'ciao', timestamp: 13 } ]
{ single: true, timestamp: 13 }
{ timestamp: 13 }
{ timestamp: 13 }

2 Comments

I tried using this with Node JS, but Object.getOwnPropertyNames is not a function will crash the app.
I'm using this with some oldish v8 engine within plv8 PostgreSQL extension. And it works in browsers. Just find a way in your JS engine to do the equivalent thing as "hasOwnProperty". Then you can reuse this logic.
0

Here is the best solution.

obj1.unshift.apply( obj1, obj2 );

Also, obj1 can grow inside a loop without any problem. (lets say obj2 is dynamic)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.