Ben
Ben

Reputation: 5361

How can I merge javascript two objects but only update properties that change

I'm trying to write a function that will let me update my kitchen in this example below. Using something like underscore's extend, however, blows away the beer in my fridge as it's updating the entire fridge object.

Is there a simple method I can use so that I only make updates with the properties that change in my changeKitchen object and not update the entire fridge object?

// My original kitchen
var kitchen = {
  fridge: {
    beer: true,
    celery: false
  },
  cabinets: {
    candy: true
  }
};


// updates I would like to make to my kitchen
var changeKitchen = {
  fridge: {
    celery: true
  }
};


var updatedKitchen = _.extend(kitchen, changeKitchen);

console.log(updatedKitchen);

Returns

var kitchen = {
  fridge: {
    celery: false // beer is gone
  },
  cabinets: {
    candy: true
  }
};

However I would like to have:

var kitchen = {
  fridge: {
    beer: true,
    celery: true // changed
  },
  cabinets: {
    candy: true
  }
};

Upvotes: 2

Views: 10373

Answers (4)

Nina Scholz
Nina Scholz

Reputation: 386604

Here is a solution in plain Javascript:

var kitchen = { fridge: { beer: true, celery: false }, cabinets: { candy: true } },
    changeKitchen = { fridge: { celery: true } };

function update(source, target) {
    Object.keys(target).forEach(function (k) {
        if (typeof target[k] === 'object') {
            source[k] = source[k] || {};
            update(source[k], target[k]);
        } else {
            source[k] = target[k];
        }
    });
}

update(kitchen, changeKitchen);
document.write('<pre>' + JSON.stringify(kitchen, 0, 4) + '</pre>');

Upvotes: 1

guest271314
guest271314

Reputation: 1

Try using for..in loop , Object.keys()

// My original kitchen
var kitchen = {
  fridge: {
    beer: true,
    celery: false
  },
  cabinets: {
    candy: true
  }
};

var changeKitchen = {
  fridge: {
    celery: true
  }
};

for (var prop in changeKitchen) {
  if (kitchen[prop]) {
    kitchen[prop]
      [Object.keys(changeKitchen[prop])] = changeKitchen[prop]
         [Object.keys(changeKitchen[prop])]
  }
}

console.log(kitchen)

Upvotes: 0

Andy
Andy

Reputation: 63524

Use _.extend on the nested objects instead:

function update(obj1, obj2) {
  for (var p in obj2) {
    if (obj1[p]) {
        _.extend(obj1[p], obj2[p]);
    } else {
      obj1[p] = obj2[p];
    }
  }
}

update(kitchen, changeKitchen);

DEMO

Upvotes: 2

Merott
Merott

Reputation: 7379

Since you're already using Underscore, you could use Lodash instead, which is a superset of Underscore, and use its #merge function.

See this question about Differences between Lodash and underscore

// My original kitchen
var kitchen = {
  fridge: {
    beer: true,
    celery: false
  },
  cabinets: {
    candy: true
  }
};


// updates I would like to make to my kitchen
var changeKitchen = {
  fridge: {
    celery: true
  }
};


var updatedKitchen = _.merge(kitchen, changeKitchen);

document.write(JSON.stringify(updatedKitchen));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>

Upvotes: 4

Related Questions