JavaScriptでは、配列内の項目を見つけるための便利な方法がたくさんあります。基本的なfor
ループをいつでも使用することはできますが、ES6以降のバージョンでは、配列をループして必要な項目を簡単に見つけることができるメソッドがたくさんあります。
非常に多くのメソッドがある中で、どのメソッドをどのような場合に使用しますか?たとえば、配列を検索するとき、その要素が配列内にあるかどうかを知りたいですか?要素のインデックスまたは要素自体が必要ですか?
ここで取り上げるさまざまなメソッドに対して、これらはすべてArray.prototypeの組み込みメソッドであることを理解することが重要です。つまり、ドット表記を使用して、任意の配列にチェーンするだけです。そして、これらのメソッドは、オブジェクトや配列以外では使用できません(ただし、文字列と重複しています)。
次の配列メソッドについて説明します。
includes
const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];
alligator.includes("thick scales"); // returns true
.includes()
メソッドは、ブール型の値を返し、要素が配列に存在するかどうかを示すのに最適です。これは、単純にtrue
またはfalse
の答えを返します。基本的な構文は次のとおりです。
arr.includes(valueToFind, [fromIndex]);
この例で見たように、valueToFindという1つのパラメータしかありませんでした。これは、配列で照合する値です。オプションのfromIndexは、検索を開始するインデックスを示す数値です(デフォルトは0なので、配列全体が検索されます)。したがって、この例では「thick scales」項目はインデックス0にあるため、次の検索ではfalseが返されます。alligator.includes('thick scales', 1);
これは、インデックス1以降から検索を開始するためです。
ここで、注意すべき重要なことがいくつかあります。この.includes()
メソッドは、厳密な比較を行います。つまり、上記の例を用いると、次の検索ではfalseが返されます。alligator.includes('80');
これは、80 == '80'
がtrueであるにもかかわらず、80 === '80'
がfalseであるためです。異なる型は厳密な比較の条件を満たしません。
find
find()
とincludes()メソッドとの違いは何ですか?この例で、「includes」というテキストのみを「find」に変更した場合、次のエラーが表示されます。
Uncaught TypeError: thick scales is not a function
これは、findメソッドでは、関数を渡す必要があるためです。findメソッドは、「includes()」のような単純な比較演算子を使用するだけではないためです。代わりに、各要素を関数に渡し、それがtrueまたはfalseを返すかどうかを確認します。したがって、alligator.find(() => 'thick scales');
は機能しますが、関連する値を返すために関数に独自の比較演算子を入れることができます。
const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];
alligator.find(el => el.length < 12); // returns '4 foot tail'
findメソッドのこの単純な関数は、割り当てた「el」のエイリアスを使用して配列の各要素を調べ、trueである最初の要素を見つけると停止します。この場合、trueは12未満の長さプロパティを持っている要素です(数値には長さプロパティはありません)。もちろん、この関数を必要に応じて複雑にすることで、正確な条件でニーズを満たすことができます。
また、これはtrue
を返さなかったことにも注意してください。findメソッドは、ブール型を返しませんが、代わりに最初に一致する要素を返します。関数で定義された条件を満たす要素が存在しない場合など、一致する要素がない場合は、undefined
を返します。また、最初のインスタンスを返すため、配列内に条件を満たす要素が複数ある場合は、最初のインスタンスのみを取得することに注意してください。この例では、「4 feet tall」の後に12未満の長さの別の文字列があったとしても、結果は変わりません。
この例では、1つのパラメータを持つコールバックのみを使用しました。パラメータを追加して、現在の要素のインデックスを参照することもできます。もう一つのパラメータは、配列全体を指定することもできますが、あまり使用されていません。インデックスを使用した例は次のとおりです。
alligator.find((el, idx) => typeof el === "string" && idx === 2); // returns '4 foot tall'
この配列では、最初の条件(typeof el === ‘string’)を満たす3つの異なる要素があります。これが唯一の条件であれば、最初の文字列である「thick scales」を返します。しかし、違いは、インデックスが2を満たす文字列は1つで、それは「4 foot tall」だけであるということです。
インデックスといえば、同様の配列メソッドで.findIndex()
があります。このメソッドも関数を受け取りますが、ご想像のとおり、要素自体ではなく、一致する要素のインデックスを返します。
indexOf
const alligator = ["thick scales", 80, "4 foot tail", "rounded snout"];
alligator.indexOf("rounded snout"); // returns 3
.indexOf()
は、.find()
メソッドで見たような関数ではなく、.includes()
メソッドと同様に、厳密な比較を行います。しかし、includes()
とは 異なり、ブール型ではなく、要素のインデックスを返します。検索を開始する配列内のインデックスを指定することもできます。
indexOf()
は非常に便利であることがわかります。すばやく簡単に配列内の要素の位置を示し、要素が存在するかどうかを確認することができます。要素が存在するかどうかは、どのようにしてわかりますか?基本的に、正の数を返す場合は要素が存在することがわかり、-1を返す場合は要素が存在しないことがわかります。
alligator.indexOf("soft and fluffy"); // returns -1
alligator.indexOf(80); // returns 1
alligator.indexOf(80, 2); // returns -1
ご覧のとおり、find()
またはfindIndex()
メソッドを使用して同じ情報を取得することはできますが、この方がはるかに少ない記述で済みます。すでにindexOf
メソッド内にあるため、比較のために関数を記述する必要はありません。
他のメソッドと同様に、indexOf()
は、最初に一致した要素のインデックスを返します。JavaScriptには、異なる配列メソッド.lastIndexOf()
があります。ご想像のとおり、これはindexOf()
と同じ動作を行いますが、配列の最後のインデックスから始まり、逆方向に動作します。2番目のパラメータを指定することもできますが、別のメソッドを使用しているという理由だけで、インデックスは変更されないことに注意してください。
const alligator = ["thick scales", 80, "4 foot tail", "rounded snout", 80];
alligator.indexOf(80); // returns 1
alligator.lastIndexOf(80); // returns 4
alligator.indexOf(80, 2); // returns 4
alligator.lastIndexOf(80, 4); // returns 4
alligator.lastIndexOf(80, 3); // returns 1
追加: filter
const alligator = ["thick scales", 80, "4 foot tail", "rounded snout", 80];
alligator.filter(el => el === 80); //returns [80, 80]
filter()
メソッドは、渡す関数と返される要素の条件を必要とするという点で、find()
メソッドに似ています。主な違いは、filter()
は一致する要素が1つしかない場合でも、常に配列を返します。find()
が最初に一致した要素のみを返すのに対して、これは一致するすべての要素を返します。
filterの使用に関して重要な点は、条件に一致するすべての要素を返すということです。筆者だけかもしれませんが、正直なところ、フィルタを適用(in
)したい要素が指定してあると、「これらはフィルターで除外(out
)したい要素です」と考えて、混乱することがあります。
まとめ
何かを検索する際に一番簡単な方法は find()
メソッドですが、お分かりのように、 それぞれのケースにより異なります。
- 存在するかどうかだけを知りたいですか?
.includes()
を使用します。 - 要素自体を取得する必要がありますか?
.find()
または、複数の項目に対して.filter()
を使用します。 - 要素のインデックスを検索する必要がありますか?
.indexOf()
または、より複雑な検索に対してfindIndex()
を使用します。
ここで扱った配列の例は、非常に単純なものでした。オブジェクトの配列を見かけることがあるかもしれません。複雑にネストされたオブジェクトを見て回るための非常に基本的な例を以下に示します。
const jungle = [
{ name: "frog", threat: 0 },
{ name: "monkey", threat: 5 },
{ name: "gorilla", threat: 8 },
{ name: "lion", threat: 10 }
];
// break the object down in order to use .includes() or .indexOf()
const names = jungle.map(el => el.name); // returns ['frog', 'monkey', 'gorilla', 'lion']
console.log(names.includes("gorilla")); // returns true
console.log(names.indexOf("lion")); // returns 3 - which corresponds correctly assuming no sorting was done
// methods we can do on the array of objects
console.log(jungle.find(el => el.threat == 5)); // returns object - {name: "monkey", threat: 5}
console.log(jungle.filter(el => el.threat > 5)); // returns array - [{name: "gorilla", threat: 8}, {name: 'lion', threat: 10}]