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

JavaScriptで入れ子構造のJSONから一致するすべての都市データを検索して返す方法

はじめに

Webアプリケーション開発では、国・州・都市のように階層化されたJSONデータを扱う場面が少なくありません。本記事では、入れ子構造を持つJSONオブジェクトの中から、指定した検索文字列に一致するすべての都市オブジェクトを配列として取得するJavaScript関数の実装方法を、サンプルコードと実行結果とあわせて解説します。

扱うデータ構造の確認

今回の例では、country(国)→ province(州)→ city(都市)という3階層の入れ子構造を持つオブジェクトを使用します。中身は次のようになっています。

const countryInfo = {
    country: [{
        name: "Bangladesh",
        province: [{
            name: "Dhaka",
            city: [{
                name: "Tangail",
                lat: '11'
            }, {
                name: "Jamalpur",
                lat: '12'
            }]
        }, {
            name: "Khulna",
            city: [{
                name: "Jossore",
                lat: '22'
            }, {
                name: "Tangail",
                lat: '23'
            }]
        }, {
            name: "Rajshahi",
            city: [{
                name: "Pabna",
                lat: '33'
            }, {
                name: "Rangpur",
                lat: '33'
            }]
        }]
    }, {
        name: "India",
        province: [{
            name: "West Bengal",
            city: [{
                name: "Calcutta",
                lat: '111'
            }, {
                name: "Tangail",
                lat: '112'
            }]
        }, {
            name: "Uttar Pradesh",
            city: [{
                name: "Agra",
                lat: '122'
            }, {
                name: "Tajmahal",
                lat: '123'
            }]
        }, {
            name: "Rajasthan",
            city: [{
                name: "Kanpur",
                lat: '131'
            }, {
                name: "Jaypur",
                lat: '132'
            }]
        }]
    }]
};

注目すべきは、「Tangail」という都市名がDhaka州・Khulna州・West Bengal州の3か所に存在している点です。単純な検索では最初の1件しかヒットしませんが、今回は条件に一致したすべての要素を漏れなく取得できることが目標です。

実装の方針

処理は大きく次の2段階に分けると理解しやすくなります。

  1. 平坦化:reduce()forEach()を使い、各州に分散している都市オブジェクトを1つの配列にまとめる
  2. 絞り込み:filter()を使い、nameプロパティが検索文字列と一致する要素だけを抽出する

「まず集めて、それから絞り込む」というシンプルな流れにすることで、階層が深いデータでも読みやすく保守しやすいコードになります。

サンプルコード

上記の方針に沿って実装したのが次のsearchForCity関数です。第1引数に元のオブジェクト、第2引数に検索文字列を渡すと、一致した都市オブジェクトの配列が返ります。

const searchForCity = (obj, query) => {
    // ステップ1:都市オブジェクトだけを1つの配列にまとめる(平坦化)
    const cities = obj.country.reduce((acc, val) => {
        val.province.forEach(prov => {
            prov.city.forEach(city => {
                acc.push(city);
            });
        });
        return acc;
    }, []);

    // ステップ2:nameが検索文字列と完全一致する都市だけを抽出
    const res = cities.filter(city => {
        return city.name === query;
    });

    return res;
};

console.log(searchForCity(countryInfo, 'Tangail'));

実行結果

searchForCity(countryInfo, 'Tangail')を実行すると、コンソールには次のように出力されます。Bangladesh内の2件とIndia内の1件、合計3件すべてが正しく取得できています。

[
    { name: 'Tangail', lat: '11' },
    { name: 'Tangail', lat: '23' },
    { name: 'Tangail', lat: '112' }
]

応用:部分一致で検索したい場合

都市名の一部だけわかっている状態で検索したいケースもあるでしょう。その場合は、絞り込み条件を===による完全一致から、includes()を使った部分一致へ変更するだけで対応できます。

const res = cities.filter(city => city.name.includes(query));

このように、reduce()による平坦化とfilter()による絞り込みを組み合わせれば、どれだけ深い入れ子構造のデータでも柔軟な検索処理を実装できます。ぜひご自身のプロジェクトでも活用してみてください。

  1. JavaScriptにおけるpreventDefault()とreturn falseの違いを徹底解説

    preventDefault()とreturn falseの基本的な違いpreventDefault()は、イベントが発生した際にブラウザのデフォルト動作をキャンセルするメソッドです。例えば、リンクをクリックしたときのページ遷移を防ぎたい場合などに使用します。ただし、イベント自体の伝播(バブリング)は止められず、後続の処理も通常どおり実行されます。一方、return falseは、インラインのイベントハンドラやjQueryなどの環境で使われた場合、デフォルト動作を停止するだけでなく、イベントの伝播も阻止します。また、関数内でreturn falseに到達した時点でそれ以降の処理は実行されず、即

  2. JavaScriptで動的インポート(dynamic import)をawaitする方法

    この記事では、JavaScriptの動的インポート(dynamic import)をawaitを使って読み込む方法について解説します。 注意: このサンプルコードは、モジュールを読み込むためローカルサーバー(localhost)環境が必要です。HTMLファイルを直接開くのではなく、Live Serverなどの開発用サーバー経由で実行してください。 動的インポートとは? 通常のimport文はファイルの先頭で静的に宣言しますが、import()関数を使うと、必要なタイミング(例:ボタンクリック時)でモジュールを遅延読み込みできます。戻り値はPromiseであるため、awaitと組み合わせることで