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

JavaScriptでネストされた配列をフィルタリングする方法

この記事では、入れ子(ネスト)構造を持つオブジェクトの配列に対して、特定の条件に一致する要素だけを抽出するフィルター処理を、JavaScriptで実装する方法を解説します。Array.prototype.filterArray.prototype.some を組み合わせることで、階層化されたデータも簡潔に検索できます。

問題の概要

まず、次のように legs という配列プロパティを持つオブジェクトの配列を想定します。

const arr = [{
    id: 1,
    legs:[{
        carrierName:'Pegasus'
    }]
},
{
    id: 2,
    legs:[{
        carrierName: 'SunExpress'
    },
    {
        carrierName: 'SunExpress'
    }]
},
{
    id: 3,
    legs:[{
        carrierName: 'Pegasus'
    },
    {
        carrierName: 'SunExpress'
    }]
}];

求められているのは、第一引数に上記のような配列、第二引数に検索キーワード(文字列)を受け取る関数です。この関数は、各オブジェクトの legs 内にある carrierName の値が、第二引数で指定された値と一致するオブジェクトだけを残すように配列をフィルタリングします。

たとえば第二引数が "Pegasus" だった場合、期待される出力は次のとおりです。id が 1 と 3 のオブジェクトは Pegasus を含むため残り、id が 2 のオブジェクトは除外されます。

const output = [{
    id: 1,
    legs:[{
        carrierName:'Pegasus'
    }]
},
{
    id: 3,
    legs:[{
        carrierName: 'Pegasus'
    },
    {
        carrierName: 'SunExpress'
    }]
}];

実装例

実際のコードは以下のとおりです。

const arr = [{
    id: 1,
    legs:[{
        carrierName:'Pegasus'
    }]
},
{
    id: 2,
    legs:[{
        carrierName: 'SunExpress'
    },
    {
        carrierName: 'SunExpress'
    }]
},
{
    id: 3,
    legs:[{
        carrierName: 'Pegasus'
    },
    {
        carrierName: 'SunExpress'
    }]
}];
const keys = ['Pegasus'];
const filterByKeys = (arr = [], keys = []) => {
    const res = arr.filter(function(item) {
        const thisObj = this;
        return item.legs.some(leg => {
            return thisObj[leg.carrierName];
        });
    }, keys.reduce((acc, val) => {
        acc[val] = true;
        return acc;
    }, Object.create(null)));
    return res;
}
console.log(JSON.stringify(filterByKeys(arr, keys), undefined, 4));

コードのポイント

この実装では、いくつかの重要なテクニックが使われています。

  • keys.reduce(...):検索キーワードの配列を、{ Pegasus: true } のようなルックアップ(照合用)オブジェクトに変換しています。これにより、キーの存在確認を高速に行えます。
  • filter の第2引数(thisArg)filter に第2引数を渡すことで、コールバック内の this がそのルックアップオブジェクトを参照します。アロー関数ではなく通常の関数を使っているのはこのためです。
  • item.legs.some(...)some は配列内のいずれか1つでも条件を満たせば true を返します。これにより、「複数の legs のうち1つでも一致すればオブジェクト全体を採用する」という要件を自然に表現できます。

よりシンプルな代替案

ルックアップオブジェクトを使わなくても、Array.prototype.includes を使えば同様の処理をより簡潔に書くことができます。

const filterByKeys = (arr = [], keys = []) =>
  arr.filter(item =>
    item.legs.some(leg => keys.includes(leg.carrierName))
  );

キーワードの数が少ない場合はこちらの方が読みやすく、大量のキーワードを扱う場合は Set やルックアップオブジェクトを使う方法が有利です。用途に応じて使い分けるとよいでしょう。

出力結果

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

[
    {
        "id": 1,
        "legs": [
            {
                "carrierName": "Pegasus"
            }
        ]
    },
    {
        "id": 3,
        "legs": [
            {
                "carrierName": "Pegasus"
            },
            {
                "carrierName": "SunExpress"
            }
        ]
    }
]
  1. JavaScriptにおける継承の基本を具体例で解説

    JavaScriptは、クラスベースではなくプロトタイプベースのオブジェクト指向言語です。そのため、継承はprototype(プロトタイプ)オブジェクトを通じて実現されます。コンストラクタ関数のprototypeプロパティにメソッドやプロパティを追加すると、そのコンストラクタから生成されたすべてのインスタンスが、それらを共有して利用できるようになります。 プロトタイプによる継承の仕組み JavaScriptでは、インスタンスからプロパティやメソッドが参照されるとき、まずオブジェクト自身を検索し、見つからなければプロトタイプチェーンをたどって上位のオブジェクトへと探しに行きます。これにより、各イ

  2. JavaScriptでリンクを絞り込む検索フィルターメニューの作成方法

    JavaScriptを使えば、検索ボックスへの入力に応じてリンクリストをリアルタイムに絞り込む「フィルタリング検索メニュー」を簡単に実装できます。この記事では、入力したキーワードに一致する項目だけを表示する機能を、実際に動くサンプルコードとともにわかりやすく解説します。 実装の仕組み 今回のフィルタリング機能は、次の流れで動作します。 テキストボックスでkeyupイベント(キーを離した瞬間)を検知する 入力値を取得し、toUpperCase()で大文字に統一して比較できるようにする リスト内のすべてのli要素を走査し、リンクのテキストに入力値が含まれるかをindexOf()で判定する 一致