如何使用lodash按ID合并两个对象数组?

49

我有两个数组,它们都有一个共同的字段member。如何轻松地合并它们?

例如:

var arr1 = [{
  "member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),
  "bank" : ObjectId("575b052ca6f66a5732749ecc"),
  "country" : ObjectId("575b0523a6f66a5732749ecb")
},
{
  "member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),
  "bank" : ObjectId("575b052ca6f66a5732749ecc"),
  "country" : ObjectId("575b0523a6f66a5732749ecb")
}];

var arr2 = [{
    "member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),
    "name" : 'xxxxxx',
    "age" : 25
},
{
    "member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),
    "name" : 'yyyyyyyyyy',
    "age" : 26
}];

预期结果:

var merge = [{
  "member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),
  "bank" : ObjectId("575b052ca6f66a5732749ecc"),
  "country" : ObjectId("575b0523a6f66a5732749ecb"),
  "name" : 'xxxxxx',
  "age" : 25
},
{
  "member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),
  "bank" : ObjectId("575b052ca6f66a5732749ecc"),
  "country" : ObjectId("575b0523a6f66a5732749ecb"),
  "name" : 'yyyyyyyyyy',
  "age" : 26
}];

我尝试了

var merge = _.unionBy(arr1, arr2, 'member');

但是预期的合并没有发生。显示了数组1的值。有人能帮我吗?


1
http://youmightnotneedjquery.com/#deep_extend 或 http://youmightnotneedjquery.com/#extend,这两个链接指向 https://lodash.com/docs#assign。 - Simon Hänisch
2
我记得你之前曾经问过这个问题(https://dev59.com/cVkT5IYBdhLWcg3wf_pN),我认为最受欢迎的答案应该已经足够回答了。只需将 memberID 更改为 member,并在连接中删除 arr3arr4 即可。 - ryeballar
1
你之前得到的答案比你想象的更有价值。不要要求我们再次发布它,只需将其标记为重复即可。 - 4castle
2个回答

64

如果两个数组都按正确顺序排列,其中每个项对应其关联的成员标识符,则可以简单地使用。

var merge = _.merge(arr1, arr2);

哪个是以下缩写:

var merge = _.chain(arr1).zip(arr2).map(function(item) {
    return _.merge.apply(null, item);
}).value();

或者,如果数组中的数据没有特定顺序,你可以通过成员值查找关联的项。

var merge = _.map(arr1, function(item) {
    return _.merge(item, _.find(arr2, { 'member' : item.member }));
});

您可以轻松将此转换为混合器。请参阅以下示例:

_.mixin({
  'mergeByKey' : function(arr1, arr2, key) {
    var criteria = {};
    criteria[key] = null;
    return _.map(arr1, function(item) {
      criteria[key] = item[key];
      return _.merge(item, _.find(arr2, criteria));
    });
  }
});

var arr1 = [{
  "member": 'ObjectId("57989cbe54cf5d2ce83ff9d6")',
  "bank": 'ObjectId("575b052ca6f66a5732749ecc")',
  "country": 'ObjectId("575b0523a6f66a5732749ecb")'
}, {
  "member": 'ObjectId("57989cbe54cf5d2ce83ff9d8")',
  "bank": 'ObjectId("575b052ca6f66a5732749ecc")',
  "country": 'ObjectId("575b0523a6f66a5732749ecb")'
}];

var arr2 = [{
  "member": 'ObjectId("57989cbe54cf5d2ce83ff9d8")',
  "name": 'yyyyyyyyyy',
  "age": 26
}, {
  "member": 'ObjectId("57989cbe54cf5d2ce83ff9d6")',
  "name": 'xxxxxx',
  "age": 25
}];

var arr3 = _.mergeByKey(arr1, arr2, 'member');

document.body.innerHTML = JSON.stringify(arr3, null, 4);
body { font-family: monospace; white-space: pre; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.0/lodash.min.js"></script>


5
ňťĘ_.mapňćůńŻ┐šöĘ_.findń╝ÜňłŤň╗║ńŞÇńެO(n^2)šÜäŔžúňć│Šľ╣ŠíłŃÇé - 4castle
2
还要注意,此答案将省略在arr1中不存在的arr2元素。建议使用Ori Drori的答案。 - joniba
完全不起作用,最终我得到的是最后一个数组的值,而不是两个合并数组的值。 - Phil

56
使用_.keyBy()为两个数组创建字典,合并这些字典,并使用_.values()将结果转换为数组。这样,数组的顺序不重要。此外,它还可以处理长度不同的数组。

const ObjectId = (id) => id; // mock of ObjectId
const arr1 = [{"member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),"bank" : ObjectId("575b052ca6f66a5732749ecc"),"country" : ObjectId("575b0523a6f66a5732749ecb")},{"member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),"bank" : ObjectId("575b052ca6f66a5732749ecc"),"country" : ObjectId("575b0523a6f66a5732749ecb")}];
const arr2 = [{"member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),"name" : 'xxxxxx',"age" : 25},{"member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),"name" : 'yyyyyyyyyy',"age" : 26}];

const merged = _(arr1) // start sequence
  .keyBy('member') // create a dictionary of the 1st array
  .merge(_.keyBy(arr2, 'member')) // create a dictionary of the 2nd array, and merge it to the 1st
  .values() // turn the combined dictionary to array
  .value(); // get the value (array) out of the sequence

console.log(merged);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.0/lodash.min.js"></script>

使用 ES6 Map

将数组连接起来,然后用reduce将组合数组减少到一个 Map。使用Object#assign将具有相同成员的对象组合为一个新对象,并存储在 Map 中。使用Map#valuesspread将 Map 转换为数组:

const ObjectId = (id) => id; // mock of ObjectId
const arr1 = [{"member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),"bank" : ObjectId("575b052ca6f66a5732749ecc"),"country" : ObjectId("575b0523a6f66a5732749ecb")},{"member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),"bank" : ObjectId("575b052ca6f66a5732749ecc"),"country" : ObjectId("575b0523a6f66a5732749ecb")}];
const arr2 = [{"member" : ObjectId("57989cbe54cf5d2ce83ff9d6"),"name" : 'xxxxxx',"age" : 25},{"member" : ObjectId("57989cbe54cf5d2ce83ff9d8"),"name" : 'yyyyyyyyyy',"age" : 26}];

const merged = [...arr1.concat(arr2).reduce((m, o) => 
  m.set(o.member, Object.assign(m.get(o.member) || {}, o))
, new Map()).values()];

console.log(merged);


这个可以工作,而 lodash 则失败了,只返回最后一个数组的内容。 - Phil
如果数组大小始终相同,则可以按ID、邮政编码、映射进行排序。 - repo
我认为这可能会打破顺序,因为JS对象/Map对象不保证任何顺序。 - magom001
Map的顺序是有保证的,并且在ES6中,对象的迭代顺序也是有保证的。 - Ori Drori

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接