68

在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它来执行此操作,但它只是将第二个数组中的值覆盖到第一个数组中

4

18 回答 18

44

这应该可以解决问题:

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

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

于 2013-10-20T17:03:27.103 回答
41

简短的 ES6 解决方案

const a3 = a1.map(t1 => ({...t1, ...a2.find(t2 => t2.id === t1.id)}))
于 2020-05-08T01:41:54.130 回答
24

假设 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());
于 2015-05-19T20:04:37.683 回答
15

ES6 简化了这一点:

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

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

例子:

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,而不是Underscore

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}
于 2018-11-28T10:54:21.560 回答
10

减少版本。

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

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

于 2016-01-23T12:49:16.060 回答
5

已经有很多很好的答案,我将添加另一个来自我昨天需要解决的实际问题的答案。

我有一组带有用户 ID 的消息,以及一组包含用户名和其他详细信息的用户。这就是我设法将用户详细信息添加到消息中的方式。

var messages = [{userId: 2, content: "Salam"}, {userId: 5, content: "Hello"},{userId: 4, content: "Moi"}];
var users = [{id: 2, name: "Grace"}, {id: 4, name: "Janetta"},{id: 5, name: "Sara"}];

var messagesWithUserNames = messages.map((msg)=> {
  var haveEqualId = (user) => user.id === msg.userId
  var userWithEqualId= users.find(haveEqualId)
  return Object.assign({}, msg, userWithEqualId)
})
console.log(messagesWithUserNames)
于 2017-07-27T19:16:53.157 回答
4

香草JS解决方案

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

const merge = (arr1, arr2) => {
  const temp = []

  arr1.forEach(x => {
    arr2.forEach(y => {
      if (x.id === y.id) {
        temp.push({ ...x, ...y })
      }
    })
  })

  return temp
}

console.log(merge(a1, a2))
于 2019-07-24T11:51:51.170 回答
4

lodash 实现:

var merged = _.map(a1, function(item) {
    return _.assign(item, _.find(a2, ['id', item.id]));
});

结果:

[  
   {  
      "id":1,
      "name":"test",
      "count":"1"
   },
   {  
      "id":2,
      "name":"test2",
      "count":"2"
   }
]
于 2016-09-03T09:28:19.673 回答
2
const a3 = a1.map(it1 => {
   it1.test = a2.find(it2 => it2.id === it1.id).test
   return it1
 })
于 2019-06-19T03:02:49.150 回答
2

一个工作的 TypeScript 版本:

export default class Merge {
  static byKey(a1: any[], a2: any[], key: string) {
    const res = a1.concat(a2).reduce((acc, x) => {
      acc[x[key]] = Object.assign(acc[x[key]] || {}, x);
      return acc;
    }, {});

    return Object.entries(res).map(pair => {
      const [, value] = pair;
      return value;
    });
  }
}

test("Merge", async () => {
  const a1 = [{ id: "1", value: "1" }, { id: "2", value: "2" }];
  const a2 = [{ id: "2", value: "3" }];

  expect(Merge.byKey(a1, a2, "id")).toStrictEqual([
    {
      id: "1",
      value: "1"
    },
    { id: "2", value: "3" }
  ]);
});

于 2019-05-30T05:45:14.600 回答
2

尝试这个

var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
let arr3 = a1.map((item, i) => Object.assign({}, item, a2[i]));

console.log(arr3);
于 2020-03-07T20:28:14.653 回答
1

想添加这个来自上面@daisihi 答案的答案。主要区别在于它使用了扩展运算符。此外,最后我删除了 id,因为它一开始是不可取的。

const a3 = [...a1, ...a2].reduce((acc, x) => {
   acc[x.id] = {...acc[x.id] || {}, ...x};
   return acc;
}, {});

这部分取自另一个帖子。从数组中的对象列表中删除属性

const newArray = Object.values(a3).map(({id, ...keepAttrs}) => keepAttrs);
于 2019-12-28T23:51:50.947 回答
1

这个怎么样?

const mergeArrayObjects = (arr1: any[], arr2: any[], mergeByKey: string): any[] => {
  const updatedArr = [];
  for (const obj of arr1) {
    const arr1ValueInArr2 = arr2.find(
      a => a[mergeByKey] === obj[mergeByKey],
    );
    if (arr1ValueInArr2) {
      updatedArr.push(Object.assign(obj, arr1ValueInArr2));
    } else {
      updatedArr.push(obj);
    }
  }
  const mergeByKeyValuesInArr1 = arr1.map(a => a[mergeByKey]);
  const remainingObjInArr2 = arr2.filter(a => !mergeByKeyValuesInArr1.includes(a[mergeByKey]) )
  return updatedArr.concat(remainingObjInArr2)
}
于 2020-12-02T06:16:35.813 回答
0

您可以像这样编写一个简单的对象合并函数

function mergeObject(cake, icing) {
    var icedCake = {}, ingredient;
    for (ingredient in cake)
        icedCake[ingredient] = cake[ingredient];
    for (ingredient in icing)
        icedCake[ingredient] = icing[ingredient];
    return icedCake;
}

接下来,您需要使用双循环将其应用于您的数据结构

var i, j, a3 = a1.slice();
for (i = 0; i < a2.length; ++i)                // for each item in a2
    for (j = 0; i < a3.length; ++i)            // look at items in other array
        if (a2[i]['id'] === a3[j]['id'])       // if matching id
            a3[j] = mergeObject(a3[j], a2[i]); // merge

您也可以mergeObject通过将一个参数作为空对象传递来用作简单的克隆。

于 2013-10-20T17:01:23.903 回答
0
function arrayUnique(array) {
    var a = array.concat();
    for (var i = 0; i < a.length; ++i) {
        for (var j = i + 1; j < a.length; ++j) {
            if (a[i] === a[j])
            a.splice(j--, 1);
        }
    }
    return a;
}
于 2021-12-27T09:19:11.530 回答
0
function mergeDiffs(Schedulearray1, Schedulearray2) {
    var secondArrayIDs = Schedulearray2.map(x=> x.scheduleid);
    return Schedulearray1.filter(x=> !secondArrayIDs.includes(x.scheduleid)).concat(Schedulearray2);   
}
于 2020-08-01T10:46:14.717 回答
0

如果您在两个数组中具有完全相同数量的具有相同 ID 的项目,则可以执行类似的操作。

const mergedArr = arr1.map((item, i) => {
  if (item.ID === arr2[i].ID) {
    return Object.assign({}, item, arr2[i]);
  }
});
于 2019-12-04T10:06:18.440 回答
-1

他们都没有为我工作。我自己写的:

const formatteddata=data.reduce((a1,a2)=>{

for (let t=0; t<a1.length; t++)
    {var id1=a1[t].id
            for (let tt=0; tt<a2.length; tt++)
                {var id2=a2[tt].id
                    if(id1==date2)
                      {a1[t]={...a1[t],...a2[tt]}}
                }
    }
return a1

})

适用于数组中任意数量的对象数组,具有不同的长度并且并不总是重合日期

于 2019-12-23T19:10:04.543 回答