合并 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 对象而不覆盖的主要内容,如果未能解决你的问题,请参考以下文章