Array.findの使い方、速度、使うべき理由【JavaScript】

次のような配列データがあるとします。
const a = { name: "aaa", value: 1 };
const b = { name: "bbb", value: 2 };
const array = [...new Array(9999).fill(a), b];
配列は要素を1万個持ち、要素はすべてnameとvalueプロパティを持っています。
要素の0から9999番目までのnameが"aaa"、最後の10000番目のnameが"bbb"です。
この配列から、nameが"bbb"である最初の要素のvalueを取得したい場合、どのように記述しますか?
今回はArray.prototype.find()の使い方とそのメリットを紹介します。
findの使い方
findを使わない場合
for (const element of array) {
if (element.name === "bbb") {
console.log(element.value);
}
}
上記のコードでnameが"bbb"の場合のvalueを取得できます。
ただ今回は"bbb"が1件のみなので1回だけ出力されますが、1度でいい場合は、
for (const element of array) {
if (element.name === "bbb") {
console.log(element.value);
break;
}
}
上記のようにforループからbreakする必要があります。
findを使った場合
以下のコードで、前述したforループと同様の結果が得られます。
const found = array.find((element) => element.name === "bbb");
console.log(found.value);
速度について
実行環境はChromeのバージョン96です。
条件に一致するデータが配列の最後にある場合
試行回数は100万要素を持つ配列から10ずつ取得 × 10回取得しています。
for | find | |
---|---|---|
1回目 | 13ms | 83ms |
2回目 | 13ms | 84ms |
3回目 | 13ms | 83ms |
4回目 | 13ms | 83ms |
5回目 | 13ms | 83ms |
6回目 | 13ms | 83ms |
7回目 | 13ms | 83ms |
8回目 | 13ms | 82ms |
9回目 | 13ms | 82ms |
10回目 | 13ms | 83ms |
平均 | 13ms | 83ms |
forループを使った方が、findに比べて約1/6で完了しました。
条件に一致するデータが配列の中間にある場合
次に、検索対象が配列の中間あたりに存在する場合です。データは以下のようなイメージです。
const array = [...new Array(4999).fill(a), b, ...new Array(5000).fill(a)];
試行回数は同様に100万要素 × 10 × 10です。
for | find | |
---|---|---|
1回目 | 7ms | 43ms |
2回目 | 7ms | 42ms |
3回目 | 7ms | 43ms |
4回目 | 7ms | 42ms |
5回目 | 7ms | 42ms |
6回目 | 7ms | 42ms |
7回目 | 7ms | 42ms |
8回目 | 7ms | 41ms |
9回目 | 7ms | 42ms |
10回目 | 6ms | 42ms |
平均 | 7ms | 42ms |
どちらも検索対象が最後にある場合の半分の時間で完了しました。
forループを使った方が、findに比べて約1/6で完了しました。
条件に一致するデータが配列の先頭にある場合
最後に、検索対象が配列の先頭に存在する場合です。データは以下のようなイメージです。
const array = [ b, ...new Array(9999).fill(a)];
差がつきにくいと判断し、試行回数は1億要素 × 10 × 10で実行しました。
for | find | |
---|---|---|
1回目 | 0ms | 0ms |
2回目 | 0ms | 0ms |
3回目 | 0ms | 0ms |
4回目 | 0ms | 0ms |
5回目 | 0ms | 0ms |
6回目 | 0ms | 0ms |
7回目 | 0ms | 0ms |
8回目 | 0ms | 0ms |
9回目 | 0ms | 0ms |
10回目 | 0ms | 0ms |
平均 | 0ms | 0ms |
数をどれだけ増やしても、差がつくことはありませんでした。
デモを使って実際に試す
今回検証で使用したプログラムをデモ環境に用意しました。
Chrome以外のブラウザでは今回と異なる結果が出ることもありうるので、ご自身の環境でお試しいただけます。
findの方が低速なのに、なぜ必要か
目的の明確化
forループを使用した場合、今回の「条件に一致する最初の値を取得する」という目的以外にも使用できるため、思わぬところでn^2のループ処理を記述してしまうかもしれません。
findのコールバック関数では、配列の要素全てに対して実行することが保証されていないため、コードの目的を「条件に一致する最初の値を取得する」に絞ることができます。
可読性
「find」という名前から、コードが何をしようとしているのか明確になります。
コールバック関数に慣れていないうちは、どう処理されているのかつかみにくいかもしれませんが、findを使った方が簡潔に記述でき、直感的に理解できると思います。
速度
今回検証に使用したコードは最大で1億回ループするプログラムでした。
最も時間のかかった処理で、約80ミリ秒です。
現在のJavaScript実行環境において、この処理がボトルネックとなってプログラム全体に影響を与えるということはほぼ考えられないでしょう。