简体   繁体   中英

Merge JS objects without overwriting

Suppose you have two objects:

var foo = {
    a : 1,
    b : 2
};

var bar = {
    a : 3,
    b : 4
}

What's the best way to merge them (and allow deep merging) to create this:

var foobar = {
    a : [1, 3],
    b : [2, 4]
}

Edit for question clarification: Ideally, in the case of an existing property in one and not the other, I would expect an array to still be created, for normalization purposes and to allow for further reduction of the map, however the answers I'm seeing below are more than sufficient. For the purposes of this exercise, I was only looking for string or numerical merges, so I hadn't entertained every possible situational case. If you held a gun to my head and asked me to make a choice, though, I'd say default to arrays.

Thanks all for your contributions.

This ought to do what you're looking for. It will recursively merge arbitrarily deep objects into arrays.

// deepmerge by Zachary Murray (dremelofdeath) CC-BY-SA 3.0
function deepmerge(foo, bar) {
  var merged = {};
  for (var each in bar) {
    if (foo.hasOwnProperty(each) && bar.hasOwnProperty(each)) {
      if (typeof(foo[each]) == "object" && typeof(bar[each]) == "object") {
        merged[each] = deepmerge(foo[each], bar[each]);
      } else {
        merged[each] = [foo[each], bar[each]];
      }
    } else if(bar.hasOwnProperty(each)) {
      merged[each] = bar[each];
    }
  }
  for (var each in foo) {
    if (!(each in bar) && foo.hasOwnProperty(each)) {
      merged[each] = foo[each];
    }
  }
  return merged;
}

And this one will do the same, except that the merged object will include copies of inherited properties. This probably isn't what you're looking for (as per RobG's comments below), but if that is actually what you are looking for, then here it is:

// deepmerge_inh by Zachary Murray (dremelofdeath) CC-BY-SA 3.0
function deepmerge_inh(foo, bar) {
  var merged = {};
  for (var each in bar) {
    if (each in foo) {
      if (typeof(foo[each]) == "object" && typeof(bar[each]) == "object") {
        merged[each] = deepmerge(foo[each], bar[each]);
      } else {
        merged[each] = [foo[each], bar[each]];
      }
    } else {
      merged[each] = bar[each];
    }
  }
  for (var each in foo) {
    if (!(each in bar)) {
      merged[each] = foo[each];
    }
  }
  return merged;
}

I tried it out with your example on http://jsconsole.com , and it worked fine:

deepmerge(foo, bar)
{"a": [1, 3], "b": [2, 4]}
bar
{"a": 3, "b": 4}
foo
{"a": 1, "b": 2}

Slightly more complicated objects worked as well:

deepmerge(as, po)
{"a": ["asdf", "poui"], "b": 4, "c": {"q": [1, 444], "w": [function () {return 5;}, function () {return 1123;}]}, "o": {"b": {"t": "cats"}, "q": 7}, "p": 764}
po
{"a": "poui", "c": {"q": 444, "w": function () {return 1123;}}, "o": {"b": {"t": "cats"}, "q": 7}, "p": 764}
as
{"a": "asdf", "b": 4, "c": {"q": 1, "w": function () {return 5;}}}

Presumably you would iterate over one object and copy its property names to a new object and values to arrays assigned to those properties. Iterate over subsequent objects, adding properties and arrays if they don't already exist or adding their values to existing properties and arrays.

eg

function mergeObjects(a, b, c) {
  c = c || {};
  var p;

  for (p in a) {
    if (a.hasOwnProperty(p)) {
      if (c.hasOwnProperty(p)) {
        c[p].push(a[p]);
      } else {
        c[p] = [a[p]];
      }
    }
  }
  for (p in b) {
    if (b.hasOwnProperty(p)) {
      if (c.hasOwnProperty(p)) {
        c[p].push(b[p]);
      } else {
        c[p] = [b[p]];
      }
    }
  }
  return c;
}

You could modify it to handle any number of objects by iterating over the arguments supplied, but that would make passing the object to merge into more difficult.

I just leave it here. Shallow merging with values as arrays.

 const foo = {a: 1, b: 2} const bar = {a: 2, с: 4} const baz = {a: 3, b: 3} const myMerge = (...args) => args.flatMap(Object.entries).reduce((acc, [key, value]) => { acc[key]??= [] acc[key].push(value) return acc }, {}) console.log(myMerge(foo, bar, baz)) //{ a: [1, 2, 3], // b: [2, 3], // с: [4] }
 .as-console-wrapper { max-height: 100%;important: top: 0 }

https://lodash.com/docs/3.10.1#merge

  // using a customizer callback var object = { 'fruits': ['apple'], 'vegetables': ['beet'] }; var other = { 'fruits': ['banana'], 'vegetables': ['carrot'] }; _.merge(object, other, function(a, b) { if (_.isArray(a)) { return a.concat(b); } }); // → { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM