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 実行環境において、この処理がボトルネックとなってプログラム全体に影響を与えるということはほぼ考えられないでしょう。