JavaScript 通过 id 合并对象

IT技术 javascript arrays underscore.js
2021-02-07 02:45:53

在 Javascript 中合并两个数组的正确方法是什么?

我有两个数组(例如):

var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]

我希望能够得到类似的结果:

var a3 = [{ id : 1, name : "test", count : "1"}, 
          { id : 2, name : "test2", count : "2"}]

根据“id”字段连接两个数组的位置,并且只是添加了额外的数据。

我尝试使用_.union此方法,但它只是将第二个数组中的值覆盖到第一个数组中

6个回答

这应该可以解决问题:

var mergedList = _.map(a1, function(item){
    return _.extend(item, _.findWhere(a2, { id: item.id }));
});

这假设 a1 中第二个对象的 id 应该是 2 而不是“2”

@CAOakley - 是的。是什么让你认为它不起作用?
2021-03-23 02:45:53
Lodash已弃用_.findWhere,支持_.find使用迭代器,因此请_.find(a2, { id: item.id })改用。
2021-03-24 02:45:53
如果 a2 在数组中的项目比 a1 多,这将不起作用。
2021-03-26 02:45:53
@4castle hehe lodash,当然……还有人在使用下划线吗?:P
2021-03-31 02:45:53
@AndreaPuddu_.unionBy在 Lodash 中,不是 Underscore,但很高兴知道!
2021-04-07 02:45:53

简短的 ES6 解决方案

const a3 = a1.map(t1 => ({...t1, ...a2.find(t2 => t2.id === t1.id)}))
@Gabriel 我认为要求是有两个共享 id 但具有不同属性的列表。
2021-04-02 02:45:53
它不会合并仅在 a2 中找到的 ID
2021-04-07 02:45:53

假设 ID 是字符串并且顺序无关紧要,您可以

  1. 创建哈希表。
  2. 迭代这两个数组并将数据存储在由 ID 索引的哈希表中。如果已经有一些具有该 ID 的数据,请使用Object.assign(ES6, can be polyfilled )更新它
  3. 获取一个包含哈希映射值的数组。
var hash = Object.create(null);
a1.concat(a2).forEach(function(obj) {
    hash[obj.id] = Object.assign(hash[obj.id] || {}, obj);
});
var a3 = Object.keys(hash).map(function(key) {
    return hash[key];
});

在 ECMAScript6 中,如果 ID 不一定是字符串,您可以使用Map

var hash = new Map();
a1.concat(a2).forEach(function(obj) {
    hash.set(obj.id, Object.assign(hash.get(obj.id) || {}, obj))
});
var a3 = Array.from(hash.values());

ES6 对此进行了简化:

let merge = (obj1, obj2) => ({...obj1, ...obj2});

注意重复的key会被合并,以第二个对象的值为准,忽略第一个对象的重复值

例子:

let obj1 = {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj1Val"};
let obj2 = {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj2Val"};

merge(obj1, obj2)
// {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj2Val", uniqueObj2Key: "uniqueKeyValueObj2"}
merge(obj2, obj1)
// {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj1Val", uniqueObj1Key: "uniqueKeyValueObj1"}

完整的解决方案(与Lodash,没有下划线

var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
var merge = (obj1, obj2) => ({...obj1, ...obj2});
_.zipWith(a1, a2, merge)
(2) [{…}, {…}]
   0: {id: 1, name: "test", count: "1"}
   1: {id: 2, name: "test2", count: "2"}

如果您有一组要合并的数组,您可以这样做:

var arrayOfArraysToMerge = [a1, a2, a3, a4]; //a3 and a4 are arrays like a1 and a2 but with different properties and same IDs.
_.zipWith(...arrayOfArraysToMerge, merge)
(2) [{…}, {…}]
   0: {id: 1, name: "test", count: "1", extra1: "val1", extra2: 1}
   1: {id: 2, name: "test2", count: "2", extra1: "val2", extra2: 2}
正如@ user3067860 指出的那样,这依赖于列表的顺序相同这并不是为了指出 Alberto 的答案。
2021-03-26 02:45:53
这不是问题的答案。请检查预期结果并改进您的答案,或将其删除。
2021-04-02 02:45:53
@StephanBijzitter 我更新了答案以完全满足问题的要求,您现在可以检查它:) 无论如何,我给他的只是一个工具来解决他的问题,而不是完整的答案,因此他也可以在没有复制的情况下学习一些知识/粘贴。
2021-04-04 02:45:53
如果它是一个答案,那么它就是一个答案,而不是发现答案的工具。使您的答案更好的是 A. 不使用库来获得答案和 B. 很好地解释您的答案,它可以教会人们下次如何在不复制和粘贴的情况下准确回答问题。
2021-04-04 02:45:53
如果列表的顺序不同,我认为这不起作用。
2021-04-04 02:45:53

减少版本。

var a3 = a1.concat(a2).reduce((acc, x) => {
    acc[x.id] = Object.assign(acc[x.id] || {}, x);
    return acc;
}, {});
_.values(a3);

我认为这是函数式语言的常见做法。

如果你想跳过使用 lodash,简单的方法是Object.valuesObject.keys(a3).map(k => a3[k])
2021-04-01 02:45:53