合并 JS 对象而不覆盖

Posted

技术标签:

【中文标题】合并 JS 对象而不覆盖【英文标题】:Merge JS objects without overwriting 【发布时间】:2011-11-24 20:40:35 【问题描述】:

假设你有两个对象:

var foo = 
    a : 1,
    b : 2
;

var bar = 
    a : 3,
    b : 4

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

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

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

感谢大家的贡献。

【问题讨论】:

如果 foo 有属性 c:5 但 bar 没有... foobar 是否有直接从 foo 复制的属性?还是有c:[5] 您是只对自己的财产感兴趣还是对继承的财产也感兴趣?我注意到你用 jQuery 标记它并扩展。您是否认为解决方案使用 jquery.extend 作为基础? 您应该更具体地说明您希望针对您要求解决的各种情况发生什么。深度合并是什么意思? RobG 的答案似乎正是您想要的 【参考方案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'] 

【讨论】:

我知道这个答案需要安装 Lodash,但我不明白为什么它需要被否决。这是唯一一个对我有用并且最干净的!【参考方案2】:

这应该可以满足您的需求。它将递归地将任意深度的对象合并到数组中。

// 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 的 cmets 下面),但如果这确实是您想要的,那么它就是:

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

【讨论】:

在第一个 else 之后缺少 ,但除此之外,效果很好。荣誉。 确认!接得好。对于那个很抱歉。当我复制/粘贴到 jsconsole 时,我修复了它,但忘记将它放回我的另一个缓冲区。再次感谢您指出这一点——我已修复它:) 我希望将“对象合并为数组”的函数为每个属性都有一个数组,而不是字符串、对象、函数等的混合。它也不适合继承的属性,也不处理具有自己属性的函数。 如果您正在合并两个完全不同类型的对象,那么您可能实际上希望在合并结果中包含继承的属性。另外,我看不出处理函数会如何增加这个解决方案的价值。无论如何,这听起来将是一个更具体的解决方案,而我的目标是更多的通用解决方案。如果你想要那个功能,修改上面的代码来做你想做的事情并不难。我只是想回答这个问题,而不是假设其他任何事情:) @Zach - OP 没有说应该或不应该处理什么类型的对象。指出限制或假设是一个好主意,以便 OP 可以做出更明智的评估。至于继承的属性,复制它们似乎不是一个好主意(但同样,无论哪种方式都没有明确的要求)。【参考方案3】:

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

例如

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;

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

【讨论】:

我认为这很明显,而不是问题所要求的 很好,那么你的答案是什么? OP 中没有关于如何处理深度合并的任何内容,也许会在回应中透露更多信息...... @Walkerneo - 如果(可能)提供的对象,大概 OP 不想复制继承的属性,也不想将它们添加到继承的属性中。 @Walkerneo, hasOwnProperty 只是确保它不是继承属性。 那我收回我的声明。我去看看什么是继承财产。编辑:好的,很抱歉。我忘记了。

以上是关于合并 JS 对象而不覆盖的主要内容,如果未能解决你的问题,请参考以下文章

合并两个对象而不覆盖

合并两个数组而不覆盖[重复]

合并表而不覆盖mysql phpmyadmin中的现有表

js_Object.assign(对象的合并)

js利用Map对两个数组对象进行合并去重

React useState 覆盖状态对象而不是合并 [重复]