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

JavaScriptでJSON配列を日付(date)をキーにマージする方法

JavaScriptでデータを扱っていると、「同じ種類のデータが日付ごとに別々のオブジェクトとして格納されていて、1つにまとめたい」というケースによく遭遇します。たとえば、異なるソースから取得した価格データを時系列に整理したい場合などが典型です。

ここでは、次のようなオブジェクトの配列を例に、「date」プロパティをキーとして重複する日付のオブジェクトをマージする関数の作り方を解説します。

const arr = [
  {
    "date": "2010-01-01",
    "price": 30
  },
  {
    "date": "2010-02-01",
    "price": 40
  },
  {
    "date": "2010-03-01",
    "price": 50
  },
  {
    "date": "2010-01-01",
    "price2": 45
  },
  {
    "date": "2010-05-01",
    "price2": 40
  },
  {
    "date": "2010-10-01",
    "price2": 50
  }
];

この配列を見ると、2010-01-01 のデータだけが price と price2 の両方を持っており、それ以外はどちらか一方しかありません。目標は、これらを日付ごとに統合し、欠けている側のプロパティを null で埋めた配列を作ることです。

マージ関数の実装例

const mergeArray = (arr = []) => {
  // 元の配列を壊さないようコピー
  const data = arr.slice();

  // 日付の古い順に並べ替え
  data.sort((a, b) => new Date(a.date) - new Date(b.date));

  // 日付文字列をキーにしたマップと、結果用の配列を用意
  const map = {};
  const res = [];

  data.forEach(el => {
    // 初めて登場する日付なら、雛形オブジェクトを作成して res に追加
    if (!map[el.date]) {
      map[el.date] = {
        date: el.date,
        price: null,
        price2: null
      };
      res.push(map[el.date]);
    }
    // 既存オブジェクトに現在の要素のプロパティを上書きマージ
    Object.assign(map[el.date], el);
  });

  return res;
};

console.log(JSON.stringify(mergeArray(arr), undefined, 4));

処理のポイント

  • slice() でコピーしてから sort():元の配列を直接変更しないため、関数の外側へ影響を与えません。日付はISO形式の文字列ですが、new Date() を経由して比較することで確実に時系列順になります。
  • 連想配列で重複を判定:「その日付を処理済みかどうか」をO(1)で判定できるため、配列全体を毎回走査するよりも効率的です。
  • Object.assign() でプロパティを統合:同じ日付のオブジェクトが現れるたびに、そのオブジェクトが持つプロパティだけが既存オブジェクトへ反映されます。存在しないプロパティは初期化時の null のまま残ります。

実行結果

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

[
  {
    "date": "2010-01-01",
    "price": 30,
    "price2": 45
  },
  {
    "date": "2010-02-01",
    "price": 40,
    "price2": null
  },
  {
    "date": "2010-03-01",
    "price": 50,
    "price2": null
  },
  {
    "date": "2010-05-01",
    "price": null,
    "price2": 40
  },
  {
    "date": "2010-10-01",
    "price": null,
    "price2": 50
  }
]

6件あった元のデータが5件に整理され、各オブジェクトが日付の昇順で並んでいます。2010-01-01 のエントリには price と price2 の両方が入り、それ以外は欠けている側が null になっているのが確認できます。

注意点:this を使わない方が安全

類似のサンプルコードでは、マップの代わりに this[el.date] のように this を使う例も見かけます。しかし、アロー関数内の this は定義された場所のスコープに依存するため、strict mode やESモジュール環境では undefined となりエラーの原因になることがあります。今回のように関数内のローカル変数(map)で管理すれば、どんな環境で呼び出しても安全に動作します

応用:プロパティが不定でも対応できる汎用版

price や price2 以外のキーが混在するデータでも、Map とスプレッド構文を組み合わせれば同じ発想で柔軟に対応できます。

const mergeArrayGeneric = (arr = []) => {
  const data = [...arr].sort((a, b) => new Date(a.date) - new Date(b.date));
  const map = new Map();

  data.forEach(el => {
    if (!map.has(el.date)) {
      map.set(el.date, { date: el.date, ...el }); // 最初の要素を雛形にする
    } else {
      Object.assign(map.get(el.date), el);
    }
  });

  return [...map.values()];
};

Map は挿入順が保証されるため結果の順序が安定し、スプレッド構文(...)で元オブジェクトの全プロパティを引き継げるため、キーの種類が固定でないデータにも対応可能です。用途に合わせて使い分けてください。

  1. JavaScriptで実装するマージソートとクイックソートの徹底解説

    マージソート(Merge Sort)とは マージソートは、分割統治法(Divide and Conquer)に基づいたソートアルゴリズムです。最悪計算量は O(n log n) と非常に効率的ですが、その代償として追加の O(n) のメモリ領域が必要になるという特徴があります。 ここでは、このアルゴリズムを2つの関数、mergeSort と merge を作成して実装していきます。 merge(マージ) ― 2つの引数(部分的にソートされた2つの配列)を受け取り、要素を正しい順序で挿入しながら1つの配列に結合する関数です。 mergeSort(マージソート) ― 配列の左半分と右半分に対し

  2. JavaScriptで配列をJSONに変換する方法|JSON.stringify()の使い方を解説

    JavaScriptで配列をJSON形式の文字列に変換するには、JSON.stringify()メソッドを使います。このメソッドは、配列やオブジェクトを引数として渡すだけで、JSON形式の文字列を簡単に生成できる非常に便利な機能です。 サンプルコード 以下は、ボタンをクリックすると配列がJSONに変換されて画面に表示されるシンプルなデモです。 <!DOCTYPE html> <html> <head> <style>    body {       font-family: "Segoe