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

JavaScriptでネストしたJSONオブジェクトの各エントリに一意のIDを付与する方法

この記事では、多階層にネストしたJSON形式の配列に対して、titleプロパティを持つすべてのオブジェクトに一意のidを付与する方法を解説します。重要なのは、元の配列をコピーせず、その場で直接書き換えるという点です。

前提となるデータ構造

例として、芸術分野のカテゴリを階層的に表現した次のような配列を考えます。トップレベルの「Arts」から始まり、「Performing arts」→「Music」「Dance」「Television」「Theatre」と、カテゴリが入れ子になっている点が特徴です。

const arr = [
  {
    "Arts": [
      {
        "Performing arts": [
          {
            "Music": [
              { "title": "Accompanying" },
              { "title": "Chamber music" },
              { "title": "Church music" },
              {
                "Conducting": [
                  { "title": "Choral conducting" },
                  { "title": "Orchestral conducting" },
                  { "title": "Wind ensemble conducting" }
                ]
              },
              { "title": "Early music" },
              { "title": "Jazz studies" },
              { "title": "Musical composition" },
              { "title": "Music education" },
              { "title": "Music history" },
              {
                "Musicology": [
                  { "title": "Historical musicology" },
                  { "title": "Systematic musicology" }
                ]
              },
              { "title": "Ethnomusicology" },
              { "title": "Music theory" },
              { "title": "Orchestral studies" },
              {
                "Organology": [
                  { "title": "Organ and historical keyboards" },
                  { "title": "Piano" },
                  { "title": "Strings, harp, oud, and guitar" },
                  { "title": "Singing" }
                ]
              },
              { "title": "Recording" }
            ]
          },
          {
            "Dance": [
              { "title": "Choreography" },
              { "title": "Dance notation" },
              { "title": "Ethnochoreology" },
              { "title": "History of dance" }
            ]
          },
          {
            "Television": [
              { "title": "Television studies" }
            ]
          },
          {
            "Theatre": [
              { "title": "Acting" },
              { "title": "Directing" },
              { "title": "Dramaturgy" },
              { "title": "History" },
              { "title": "Musical theatre" },
              { "title": "Playwrighting" },
              { "title": "Puppetry" }
            ]
          }
        ]
      }
    ]
  }
];

求められる処理の要件

  • titleプロパティを持つすべてのオブジェクトにidプロパティを追加すること
  • idの値そのものは問わないが、データ全体を通じて一意であること
  • 元の配列のコピーを作らず、直接(in placeで)書き換えること

実装コード

上記の要件を満たすコードがこちらです。

const addId = (id = 1) => {
  // クロージャでカウンターidを保持した再帰関数を返す
  return function recur(obj) {
    if ('title' in obj) {
      obj.id = id++;
    }
    Object.keys(obj).forEach(el => {
      Array.isArray(obj[el]) && obj[el].forEach(recur);
    });
  };
};

const mapId = arr => {
  const assign = addId(); // カウンターを初期化して再帰関数を生成
  arr.forEach(assign);    // 各トップレベル要素に再帰処理を適用
};

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

コードのポイント

  • クロージャによるID管理:addIdは高階関数で、引数id(初期値1)をクロージャ内に保持します。これにより、再帰全体を通じてIDが重複しないことが保証されます。
  • titleの有無をチェック:内部関数recurは、in演算子でオブジェクトがtitleを持つかどうかを判定し、持っていればobj.id = id++でIDを付与します。
  • 再帰的な走査:Object.keys()ですべてのキーを列挙し、値が配列であるプロパティについてはArray.isArray()で判定したうえで、各要素にrecurを再帰的に適用します。
  • コピー不要の直接変更:JavaScriptのオブジェクトは参照経由で操作されるため、関数内での変更はそのまま元の配列に反映されます。新しい配列を作る必要はありません。

実行結果

コードを実行すると、titleを持つすべてのオブジェクトに1から順に一意のidが振られた配列が出力されます。

[
  {
    "Arts": [
      {
        "Performing arts": [
          {
            "Music": [
              {
                "title": "Accompanying",
                "id": 1
              },
              {
                "title": "Chamber music",
                "id": 2
              },
              {
                "title": "Church music",
                "id": 3
              },
              {
                "Conducting": [
                  {
                    "title": "Choral conducting",
                    "id": 4
                  },
                  {
                    "title": "Orchestral conducting",
                    "id": 5
                  },
                  {
                    "title": "Wind ensemble conducting",
                    "id": 6
                  }
                ]
              },
              ...
            ]
          },
          ...
        ]
      }
    ]
  }
]

IDが付与されるのはあくまでtitleプロパティを持つオブジェクトだけです。「Music」や「Conducting」のようにカテゴリ名をキーとするオブジェクト自体にはidは追加されません。

まとめ

深くネストしたJSONデータへ一律の変換を加えたい場合、クロージャでカウンターを保持しながら再帰的に走査する手法が非常に有効です。このアプローチはID付与だけでなく、ノードのフラット化や特定条件での抽出・集計など、さまざまな処理に応用できます。

  1. JSONテキストをJavaScriptのJSONオブジェクトに変換する方法を解説

    JavaScriptでは、JSON.parse()メソッドを使用することで、JSON形式のテキスト文字列をJavaScriptオブジェクトへと変換できます。このメソッドは、APIから取得したデータやサーバーとの通信で受け取ったJSON文字列を、プログラム内で扱いやすいオブジェクトにパースする際に非常に重要な役割を果たします。 以下は、JSONテキストをJavaScriptオブジェクトに変換する具体的なコード例です。 サンプルコード <!DOCTYPE html> <html lang=ja> <head> <meta charset=UTF-8 /&

  2. 【JavaScript入門】Symbolを使ってオブジェクトごとに一意のIDを作成する方法

    はじめにJavaScriptでオブジェクトごとに一意のIDを作成したい場合、Symbol()を使うのが最も簡単かつ確実な方法です。Symbolは呼び出されるたびに必ず新しい一意の値を生成するため、たとえ同じ説明文字列を渡しても、二度と同じ値にはなりません。以下は、各オブジェクトに対して一意のIDを作成するサンプルコードです。実装例<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewp