tags:

views:

10718

answers:

10

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

Does anyone have a script for this or know of 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.

+44  A: 
for (attrname in obj2) { obj1[attrname] = obj2[attrname]; }

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.

John Millikin
Okay, kids, keep it down in here, pls.
Will
Unlocked to allow voting. I hope it won't be necessary to lock again.
Michael Myers
WHO EDITED MY LOCK??? WHY I OUGHTA... SLOWLY I TURNED, STEP BY STEP, HURF BY DURF...
Will
This doesn't work if objects have same name attributes, and you would also want to merge the attributes.
谢继雷
+1  A: 

prototype.js has this:

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

obj1.extend(obj2) will do what you want.

ephemient
You meant Object.prototype.extend? In any case, it's not a good idea to extend Object.prototype. -1.
SolutionYogi
Come to think of it, it probably should be `Object.prototype` and not `Object`... Good idea or not, this is what the `prototype.js` framework does, and so if you are using it or another framework that depends on it, `Object.prototype` will already be extended with `extend`.
ephemient
SolutionYogi
I don't mean that it's okay -- I mean that it might already be there, so you don't might not need your own `extend`.
ephemient
Who care's if its right or wrong? If it works then it works. Waiting around for the perfect solution is great except when trying to keep a contract, win new clients, or meet deadlines. Give anyone here who's passionate about programming free money and they'll eventually do everything the "right" way.
David
David, because you want your site to continue to work as browsers change.Solution Yogi, I don't think there's anything wrong with the way Prototype adds things to Object.prototype. Being able to add new methods by modifying constructor.prototype is just the way JS does OO. It's just a bit different from other OO languages you may have worked with. One exception is Ruby, which also allows you to modify existing instances and classes using extend and include (include in Ruby ~ constructor.prototype.extend in JS).
allyourcode
Mozilla's documentation indicates Object.prototype is provided exactly for this purpose: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object#Properties
allyourcode
+35  A: 

jQuery also has a utility for this.

Taken from the jQuery documentation:

var settings = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
jQuery.extend(settings, options);
// { validate: true, limit: 5, name: "bar" }
Avdi
Gosh that is named poorly. Very unlikely to find it when searching for how to merge objects.
Gregory
+5  A: 

I googled for code to merge object properties and ended up here. However since there wasn't any code for recursive merge I wrote it myself. (Maybe jQuery extend is recursive b.t.w.?) Anyhow, Hopefully someone else will find it useful as well.

(Now the code does not use Object.prototype :)

Code

/*
* Recursively merge properties of two objects 
*/
function MergeRecursive(obj1, obj2) {

  for (var p in obj2) {
    try {
      // Property in destination object set; update its value.
      if ( obj2[p].constructor==Object ) {
        obj1[p] = MergeRecursive(obj1[p], obj2[p]);

      } else {
        obj1[p] = obj2[p];

      }

    } catch(e) {
      // Property in destination object not set; create it and set its value.
      obj1[p] = obj2[p];

    }
  }

  return obj1;
}

An example

o1 = {  a : 1,
        b : 2,
        c : {
          ca : 1,
          cb : 2,
          cc : {
            cca : 100,
            ccb : 200 } } };

o2 = {  a : 10,
        c : {
          ca : 10,
          cb : 20, 
          cc : {
            cca : 101,
            ccb : 202 } } };

o3 = MergeRecursive(o1, o2);

Produces object o3 like

o3 = {  a : 10,
        b : 2,
        c : {
          ca : 10,
          cb : 20,
          cc : { 
            cca : 101,
            ccb : 202 } } };

Regards Markus

ooh extending the Object's prototype...very, very bad! http://erik.eae.net/archives/2005/06/06/22.13.54/ <= for more info
Andreas Grech
Dreas, good point, thanks for pointing it out. Actually I noticed the disadvantage with Object.prototype so actually I simply rewrote the code as a function with two arguments. This should work, don't you think? There is always something new to learn :)
For future visitors - JQuery has had a recursive extend() since 1.1.4. jQuery.extend( [deep], target, object1, [objectN] ) - from http://api.jquery.com/jQuery.extend/ .
Matt Luongo
+3  A: 

The given solutions should be modified to check source.hasOwnProperty(property) in the for..in loops before assigning - otherwise, you end up copying the properties of the whole prototype chain, which is rarely desired...

Christoph
A: 

The correct implementation in Prototype should look like this:

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

obj1 = Object.extend(obj1, obj2);
A: 

The mootools implementation is the same as Prototype's:

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

obj1 = Object.extend(obj1, obj2);
philfreo
A: 

@ Markus fine except o1 is modiifed right? then the call can be "MergeRecursive(o1, o2);"

poop-deck
This should really be a comment on the referenced answer, not a new answer.
Benji York
i would have except i didn't have enough points then for commenting. better to highlight what looked like a problem than worry that it was not perfectly placed. that can't be the reason for -1 tho?
poop-deck
A: 

For not too complicated objects you could use JSON:

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

objMerge = JSON.stringify(obj1) + JSON.stringify(obj2);
            // {"food": "pizza","car":"ford"}{"animal":"dog","car":"chevy"}
objMerge = objMerge.replace(/\}\{/, ""); //  \_ replace with comma for valid JSON

objMerge = JSON.parse(objMerge); // { food: 'pizza', animal: 'dog', car: 'chevy'}
// of same keys in both objects, the last object's value is retained_/

!!! Mind that in this example "}{" MUST NOT OCCUR within a string!!!

Algy
A: 

Is there a way to append a second object to the first object without overwriting any of the values in the first object?

Logan Best