web-dev-qa-db-ja.com

IDによるオブジェクトのJavaScriptマージ

Javascriptで2つの配列をマージする正しい方法は何ですか?

私は2つの配列を持っています(たとえば):

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」フィールドに基づいて2つの配列が結合され、追加のデータが単に追加されるだけです。

_.unionを使用してこれを実行しようとしましたが、2番目の配列の値を最初の配列に単純に上書きします

31
Tam2

これはトリックを行う必要があります:

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

これは、a1の2番目のオブジェクトのIDが「2」ではなく2であることを前提としています。

34
Gruff Bunny

IDは文字列であり、順序は重要ではないと仮定すると、次のことができます。

  1. ハッシュテーブルを作成します。
  2. 両方の配列を繰り返し、データをIDでインデックス付けされたハッシュテーブルに格納します。そのIDのデータが既にある場合は、 Object.assign (ES6、 ポリフィル )にできます。
  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());
14
Oriol

ES6はこれを簡素化します。

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

繰り返されるキーはマージされ、2番目のオブジェクトの値が優先され、最初のオブジェクトの繰り返される値は無視されますになります。

例:

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

削減 バージョン。

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

関数型言語の一般的な慣習だと思います。

7
Daishi Nakajima

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

すでに多くの素晴らしい答えがありますが、昨日解決するために必要な実際の問題から別の答えを追加します。

ユーザー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)
3
Nick

Vanilla 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))
2
Nick Olay
const a3 = a1.map(it1 => {
   it1.test = a2.find(it2 => it2.id === it1.id).test
   return it1
 })
1
quy pham

動作する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" }
  ]);
});

1
Jeff Tian

これはes6でうまくいくはずで、機能的です。

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

const mergeArray = (source, merge, by) => source.map(item => ({
  ...item,
  ...(merge.find(i => i[by] === item[by]) || {}),
}));

console.log(mergeArray(a1, a2, 'id'));
// > ​​​​​[{ id: 1, name: 'test', count: '1' }, { id: 2, name: 'test2', count: '2' }]

これは、a1の2番目のオブジェクトのIDが「2」ではなく2であることも前提としています。

0
Micah

このような単純なオブジェクトマージ関数を書くことができます

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

1つのパラメーターを空のオブジェクトとして渡すことで、mergeObjectを単純なクローンとして使用することもできます。

0
Paul S.