JavaScript
 Computer >> コンピューター >  >> プログラミング >> JavaScript

JavaScriptで2つのオブジェクトを比較しながら配列のネストされた値をグループ化する方法

この記事では、2つのオブジェクト(before と after)を比較しながら、ネストされた配列の値を1つの行にグループ化する方法を解説します。データの差分を表形式で整理したい場合などに役立つテクニックです。

入力データの例

まず、次のようなJSONオブジェクトがあるとします。before(変更前)と after(変更後)の2つの状態が含まれ、それぞれの device 配列の中に、id、price、features(機能名と価格)というネストされた構造を持っています。

const input = {
  "before": {
    "device": [
      {
        "id": "1234",
        "price": "10",
        "features": [
          { "name": "samsung", "price": "10" },
          { "name": "Apple", "price": "20" }
        ]
      },
      {
        "id": "2154",
        "price": "20",
        "features": [
          { "name": "samsung", "price": "30" },
          { "name": "Moto", "price": "40" }
        ]
      }
    ]
  },
  "after": {
    "device": [
      {
        "id": "1234",
        "price": "50",
        "features": [
          { "name": "samsung", "price": "20" },
          { "name": "Lenovo", "price": "30" }
        ]
      },
      {
        "id": "2158",
        "price": "40",
        "features": [
          { "name": "samsung", "price": "30" }
        ]
      }
    ]
  }
};

要件

ここで求められるのは、before と after の両方に同じ id が存在する場合、その id と対応する features を1つの行にまとめてグループ化することです。

イメージとしては、次のように表形式でデータを整理するイメージです。

  • id「1234」は before と after の両方に存在するため、価格と各機能の価格が同じ行に並びます。
  • id「2154」は before にのみ存在し、id「2158」は after にのみ存在するため、片側だけ値が入り、反対側は 0 になります。

このようにグループ化・並べ替えを行う関数を実装していきましょう。

実装コード

以下が実際のコード例です。

const input = {
  "before": {
    "device": [
      {
        "id": "1234",
        "price": "10",
        "features": [
          { "name": "samsung", "price": "10" },
          { "name": "Apple", "price": "20" }
        ]
      },
      {
        "id": "2154",
        "price": "20",
        "features": [
          { "name": "samsung", "price": "30" },
          { "name": "Moto", "price": "40" }
        ]
      }
    ]
  },
  "after": {
    "device": [
      {
        "id": "1234",
        "price": "50",
        "features": [
          { "name": "samsung", "price": "20" },
          { "name": "Lenovo", "price": "30" }
        ]
      },
      {
        "id": "2158",
        "price": "40",
        "features": [
          { "name": "samsung", "price": "30" }
        ]
      }
    ]
  }
};

const formatJSON = data => {
  const sub = Object.fromEntries(Object.keys(data).map(k => [k, 0]));
  return Object.values(Object.entries(data).reduce((r, [col, { device }]) => {
    device.forEach(({ id, price, features }) => {
      r[id] = r[id] || [{ id, ...sub }];
      r[id][0][col] = price;
      features.forEach(({ name, price }) => {
        let temp = r[id].find(q => q.name === name);
        if (!temp) {
          r[id].push(temp = { name, ...sub });
        }
        temp[col] = price;
      });
    });
    return r;
  }, {}));
};

console.log(formatJSON(input));

コードのポイント解説

1. 初期値オブジェクト sub の生成

Object.fromEntries(Object.keys(data).map(k => [k, 0])) の部分では、data のキー(before / after)をもとに、すべての値が 0 のオブジェクトを作成しています。これは「該当するデータが存在しない場合のデフォルト値」として使われます。

2. reduce() によるデータの集約

Object.entries(data).reduce(...) を使うことで、before と after のそれぞれの device 配列を順番に処理し、id をキーとした1つのオブジェクトに集約しています。

3. 同じ id・name の重複を回避

r[id].find(q => q.name === name) で、すでに同じ名前の feature が登録されているかを確認し、存在しない場合のみ新しいエントリを追加します。これにより、before と after で共通する機能名は1行にまとめられます。

出力結果

コンソールには次のような出力が表示されます。

[
  [
    { id: '1234', before: '10', after: '50' },
    { name: 'samsung', before: '10', after: '20' },
    { name: 'Apple', before: '20', after: 0 },
    { name: 'Lenovo', before: 0, after: '30' }
  ],
  [
    { id: '2154', before: '20', after: 0 },
    { name: 'samsung', before: '30', after: 0 },
    { name: 'Moto', before: '40', after: 0 }
  ],
  [
    { id: '2158', before: 0, after: '40' },
    { name: 'samsung', before: 0, after: '30' }
  ]
]

まとめ

この実装では、reduce と find を組み合わせることで、2つのオブジェクト間で共通する id や機能名を1つの行にグループ化できます。存在しない側のデータには 0 が自動的に設定されるため、変更前後の差分を見やすい形式で比較できるのが大きなメリットです。差分表示や履歴管理のUIなど、実務でも応用範囲の広いパターンなので、ぜひ参考にしてみてください。

  1. JavaScriptでIDごとにオブジェクトの配列をグループ化する方法

    JavaScriptでオブジェクトの配列をidなどの特定のキーごとにグループ化したい場合、reduce()メソッドを使うと簡潔に実装できます。この記事では、どのプロパティにも対応できる汎用的なgroupBy関数を作成し、実際のサンプルコードとともにその仕組みを詳しく解説します。 reduce()によるグループ化の考え方 reduce()は、配列の各要素を順番に処理しながら、1つの累積値(ここではオブジェクト)を組み立てていくメソッドです。各要素からキーの値を取り出し、その値がまだ結果オブジェクトに存在しなければ新しい配列を作成して、そこへ要素を追加していくことでグループ化を実現します。 サ

  2. JavaScriptのオブジェクト配列に配列メソッドを適用する方法

    JavaScriptでは、オブジェクトが格納された配列に対しても、通常の配列と同じようにpop()、push()、splice()などの標準的な配列メソッドをそのまま使用できます。オブジェクト配列はあくまで「配列」であるため、要素としてオブジェクトが入っていても配列操作のAPIは共通で動作します。 コード例 以下は、JavaScriptオブジェクトの配列に対して配列メソッドを使用するサンプルコードです。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8