JavaScript merging objects by id

我怕爱的太早我们不能终老 提交于 2019-12-16 20:01:16

问题


What's the correct way to merge two arrays in 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.

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


回答1:


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"




回答2:


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

  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.
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:

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



回答3:


ES6 simplifies this:

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.

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)

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}



回答4:


reduce version.

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.




回答5:


The lodash implementaiton:

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



回答6:


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.

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)



回答7:


Vanilla JS solution

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



回答8:


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



回答9:


A working TypeScript version:

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" }
  ]);
});




回答10:


You can write a simple object merging function like this

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

Next, you need to do use a double-loop to apply it to your data structre

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

You can also use mergeObject as a simple clone, too, by passing one parameter as an empty object.




回答11:


If you have exactly the same number of items in both array with same ids you could do something like this.

const mergedArr = arr1.map((item, i) => {
  if (item.ID === arr2[i].ID) {
    return Object.assign({}, item, arr2[i]);
  }
});


来源:https://stackoverflow.com/questions/19480008/javascript-merging-objects-by-id

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!