Categories
javascript javascript-objects

How can I merge properties of two JavaScript objects dynamically?

3053

I need to be able to merge two (very simple) JavaScript objects at runtime. For example I’d like to:

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

obj1.merge(obj2);

//obj1 now has three properties: food, car, and animal

Is there a built in way to do this? I do not need recursion, and I do not need to merge functions, just methods on flat objects.

2

  • Its worth noting this answer on a similar question, which shows how to merge “one level down”. That is, it merges values of duplicate keys (instead of overwriting first value with second value), but does not recurse further than that. IMHO, its good clean code for that task.

    Oct 3, 2019 at 21:42


  • BTW, the top few answers do a “shallow” merge: if the same key exists in both obj1 and obj2, the value in obj2 is kept, the value in obj1 is dropped. E.g. if question’s example had var obj2 = { animal: 'dog', food: 'bone' };, the merge would be { food: 'bone', car: 'ford', animal: 'dog' }. If you are working with “nested data”, and want a “deep merge”, then look for answers that mention “deep merge” or “recursion”. If you have values that are arrays, then use “arrayMerge” option of github “TehShrike/deepmerge”, as mentioned here.

    Oct 4, 2019 at 12:30


3688

ECMAScript 2018 Standard Method

You would use object spread:

let merged = {...obj1, ...obj2};

merged is now the union of obj1 and obj2. Properties in obj2 will overwrite those in obj1.

/** There's no limit to the number of objects you can merge.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = {...obj1, ...obj2, ...obj3};

Here is also the MDN documentation for this syntax. If you’re using babel you’ll need the babel-plugin-transform-object-rest-spread plugin for it to work.

ECMAScript 2015 (ES6) Standard Method

/* For the case in question, you would do: */
Object.assign(obj1, obj2);

/** There's no limit to the number of objects you can merge.
 *  All objects get merged into the first object. 
 *  Only the object in the first argument is mutated and returned.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = Object.assign({}, obj1, obj2, obj3, etc);

(see MDN JavaScript Reference)


Method for ES5 and Earlier

for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }

Note that this will simply add all attributes of obj2 to obj1 which might not be what you want if you still want to use the unmodified obj1.

If you’re using a framework that craps all over your prototypes then you have to get fancier with checks like hasOwnProperty, but that code will work for 99% of cases.

Example function:

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 */
function merge_options(obj1,obj2){
    var obj3 = {};
    for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}

4

  • 113

    This doesn’t work if objects have same name attributes, and you would also want to merge the attributes.

    Oct 24, 2010 at 10:56

  • 83

    This only does a shallow copy/merge. Has the potential to clobber a lot of elements.

    Jun 2, 2011 at 15:39

  • 60

    ​+1 for acknowledging that some poor souls are forced to use frameworks that crap all over their prototypes…

    May 7, 2016 at 4:18

  • 3

    Object.assign(obj1, obj2); may be the preferable way as let merged = {...obj1, ...obj2}; creates a new object and copies the the properties of obj1 and obj2 into it which may be very expensive for large objects. The assign method also modifies obj1 like the behavior expected in the question.

    – SePeF

    Nov 29, 2021 at 13:12

1232

jQuery also has a utility for this: http://api.jquery.com/jQuery.extend/.

Taken from the jQuery documentation:

// Merge options object into settings object
var settings = { validate: false, limit: 5, name: "foo" };
var options  = { validate: true, name: "bar" };
jQuery.extend(settings, options);

// Now the content of settings object is the following:
// { validate: true, limit: 5, name: "bar" }

The above code will mutate the existing object named settings.


If you want to create a new object without modifying either argument, use this:

var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };

/* Merge defaults and options, without modifying defaults */
var settings = $.extend({}, defaults, options);

// The content of settings variable is now the following:
// {validate: true, limit: 5, name: "bar"}
// The 'defaults' and 'options' variables remained the same.

2

  • 174

    Careful: the variable “settings” will be modified, though. jQuery doesn’t return a new instance. The reason for this (and for the naming) is that .extend() was developed to extend objects, rather than to munge stuff together. If you want a new object (e.g. settings is defaults you don’t want to touch), you can always jQuery.extend({}, settings, options);

    – webmat

    May 4, 2011 at 16:01


  • 30

    Mind you, jQuery.extend also has a deep (boolean) setting. jQuery.extend(true,settings,override), which is important if a property in settings holds an object and override only has part of that object. Instead of removing the unmatched properties, the deep setting will only update where it exists. The default is false.

    – vol7ron

    Jun 9, 2011 at 21:09

377

The Harmony ECMAScript 2015 (ES6) specifies Object.assign which will do this.

Object.assign(obj1, obj2);

Current browser support is getting better, but if you’re developing for browsers that don’t have support, you can use a polyfill.

4

  • 42

    Note that this is only a shallow merge

    Mar 16, 2016 at 10:04

  • I think meanwhile Object.assign has pretty decent coverage: kangax.github.io/compat-table/es6/…

    May 26, 2016 at 15:38

  • 15

    This should now be the correct answer. People nowadays compile their code to be compatible with the rare browser (IE11) that doesn’t support this kind of thing. Side note: if you don’t want to add obj2 to obj1, you can return a new object using Object.assign({}, obj1, obj2)

    – Duvrai

    Jul 21, 2016 at 20:02

  • 6

    @Duvrai According to reports I’ve seen, IE11 is definitely not rare at around 18% of the market share as of July 2016.

    Aug 8, 2016 at 19:15