合并JS对象而不覆盖

时间:2011-09-26 00:26:47

标签: javascript merge extend deep-copy

假设您有两个对象:

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

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

合并它们(并允许深度合并)的最佳方法是创建它:

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

编辑问题澄清:理想情况下,如果现有属性在一个而不是另一个中,我希望仍然可以创建一个数组,用于规范化目的并允许进一步减少地图,但我在下面看到的答案绰绰有余。出于本练习的目的,我只是寻找字符串或数字合并,所以我没有接受所有可能的情境案例。如果你拿着枪在我的头上然后让我做出选择,我会说默认为阵列。

感谢所有人的贡献。

3 个答案:

答案 0 :(得分:9)

这应该做你想要的。它会递归地将任意深度的对象合并到数组中。

// 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;
}

除了合并的对象将包含继承属性的副本之外,这个会做同样的事情。这可能不是你想要的(根据RobG在下面的评论),但如果这实际上是你正在寻找的,那么它就是:

// 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;
}

我在http://jsconsole.com上尝试了你的例子,它运行良好:

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

稍微复杂的对象也起作用了:

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;}}}

答案 1 :(得分:3)

据推测,您将迭代一个对象并将其属性名称复制到新对象,并将值复制到分配给这些属性的数组。迭代后续对象,添加属性和数组(如果它们尚不存在)或将它们的值添加到现有属性和数组中。

e.g。

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;
}

您可以通过迭代所提供的参数来修改它以处理任意数量的对象,但这会使合并的对象更难传递。

答案 2 :(得分:-1)

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'] }