JavaScript 通过 id 合并对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19480008/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 15:43:19  来源:igfitidea点击:

JavaScript merging objects by id

javascriptarraysunderscore.js

提问by Tam2

What's the correct way to merge two arrays in Javascript?

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

I've got two arrays (for example):

我有两个数组(例如):

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

I want to be able to end up with something like:

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

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

Where the two arrays are being joined based on the 'id' field and extra data is simply being added.

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

I tried to use _.unionto do this, but it simply overwrites the values from the second array into the first one

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

采纳答案by Gruff Bunny

This should do the trick:

这应该可以解决问题:

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

This assumes that the id of the second object in a1 should be 2 rather than "2"

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

回答by Oriol

Assuming IDs are strings and the order does not matter, you can

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

  1. Create a hash table.
  2. Iterate both arrays and store the data in the hash table, indexed by the ID. If there already is some data with that ID, update it with Object.assign(ES6, can be polyfilled).
  3. Get an array with the values of the hash map.
  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];
});

In ECMAScript6, if the IDs are not necessarily strings, you can use Map:

在 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());

回答by Alberto

ES6 simplifies this:

ES6 对此进行了简化:

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

Note that repeated keys will be merged, and the value of the second object will prevail and the repeated value of the first object will be ignored.

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

Example:

例子:

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"}

Complete solution (with Lodash, not Underscore)

完整的解决方案(与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"}

If you have an array of arrays to merge you can do it like this:

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

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}

回答by Daishi Nakajima

reduceversion.

减少版本。

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

I think it's common practice in functional language.

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

回答by bora89

The lodash implementaiton:

lodash 实现:

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

The result:

结果:

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

回答by Nick

Already there are many great answers, I'll just add another one which is from a real problem I needed to solve yesterday.

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

I had an array of messages with user ids, and one array of users containing users' names and other details. This is how I managed to add user details to the messages.

我有一组带有用户 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)

回答by Nick Olay

Vanilla JS solution

香草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))

回答by quy pham

const a3 = a1.map(it1 => {
   it1.test = a2.find(it2 => it2.id === it1.id).test
   return it1
 })

回答by Andreas Tzionis

Short ES6 solution

简短的 ES6 解决方案

const a3 = a1.map(t1 => ({...t1, ...a2.find(t2 => t2.id === t1.id)}))

回答by Jeff Tian

A working TypeScript version:

一个有效的 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" }
  ]);
});