- JavaScript
JavaScript配列の検索|find・includes・some・every
JavaScriptの配列検索は「何がほしいか」でメソッドが決まります。あるかどうかを知りたいなら includes か some、要素そのものがほしいなら find、位置がほしいなら indexOf か findIndex、条件に合うものを全部ほしいなら filter です。
この記事では、検索に使う10個のメソッド(find・findIndex・findLast・findLastIndex・includes・indexOf・lastIndexOf・some・every・filter)を1枚の表で比べ、目的別の逆引きを付けました。さらに、NaN、空配列、オブジェクトの比較といったつまずきやすい点と、件数が多いときに Set・Map へ置き換えた場合の計測値を、Node.js 22.22.2 で実際に動かした結果つきで載せています。
配列検索メソッドの早見表
10メソッドの比較表
| メソッド | 引数 | 戻り値 | 見つからないとき | 途中で打ち切るか |
|---|---|---|---|---|
find(fn) |
条件の関数 | 最初に合った要素 | undefined | 見つかった時点で終了 |
findIndex(fn) |
条件の関数 | 最初に合った位置 | -1 | 見つかった時点で終了 |
findLast(fn) |
条件の関数 | 後ろから探して最初に合った要素 | undefined | 見つかった時点で終了 |
findLastIndex(fn) |
条件の関数 | 後ろから探して最初に合った位置 | -1 | 見つかった時点で終了 |
includes(値, 開始位置) |
値 | true / false | false | 見つかった時点で終了 |
indexOf(値, 開始位置) |
値 | 最初の位置 | -1 | 見つかった時点で終了 |
lastIndexOf(値, 開始位置) |
値 | 最後の位置 | -1 | 後ろから探し、見つかった時点で終了 |
some(fn) |
条件の関数 | 1つでも合えば true | false | true になった時点で終了 |
every(fn) |
条件の関数 | 全部合えば true | false(空配列は true) | false になった時点で終了 |
filter(fn) |
条件の関数 | 合った要素の新しい配列 | 空の配列 [] |
最後まで調べる |
大きく分けると「値を渡すグループ(includes・indexOf・lastIndexOf)」と「条件の関数を渡すグループ(それ以外)」の2つです。 数値や文字列をそのまま探すなら前者、オブジェクトの配列や「10以上」のような条件なら後者を選びます。
目的別の逆引き
| 知りたいこと・ほしいもの | 値で探す(数値・文字列) | 条件で探す(オブジェクトなど) |
|---|---|---|
| あるかどうか知りたい | arr.includes(値) |
arr.some(fn) |
| 全部が条件を満たすか知りたい | ― | arr.every(fn) |
| 1つも満たさないことを確かめたい | !arr.includes(値) |
!arr.some(fn) |
| 要素がほしい(最初の1件) | ― | arr.find(fn) |
| 要素がほしい(最後の1件) | ― | arr.findLast(fn) |
| 位置がほしい(最初) | arr.indexOf(値) |
arr.findIndex(fn) |
| 位置がほしい(最後) | arr.lastIndexOf(値) |
arr.findLastIndex(fn) |
| 全部ほしい | arr.filter(x => x === 値) |
arr.filter(fn) |
| 件数がほしい | arr.filter(x => x === 値).length |
arr.filter(fn).length |
| 何度も探す(件数が多い) | new Set(arr) を作って has |
new Map(...) を作って get |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「JavaScript配列の検索|find・includes・some・every」
10メソッドを同じデータで動かして比べる
コピペ用のサンプルと実行結果
以下のデータを使います。ファイルに保存して node table.mjs で動かせます。
// table.mjs (Node.js 22.22.2 で確認)
const users = [
{ id: 1, name: 'Aoki', role: 'admin', active: true },
{ id: 2, name: 'Baba', role: 'member', active: false },
{ id: 3, name: 'Chiba', role: 'member', active: true },
{ id: 4, name: 'Doi', role: 'guest', active: true },
];
const nums = [5, 12, 8, 12, 3];
console.log(users.find(u => u.role === 'member'));
console.log(users.find(u => u.role === 'owner'));
console.log(users.findIndex(u => u.role === 'member'));
console.log(users.findIndex(u => u.role === 'owner'));
console.log(users.findLast(u => u.role === 'member'));
console.log(users.findLastIndex(u => u.role === 'member'));
console.log(nums.includes(12), nums.includes(99));
console.log(nums.indexOf(12), nums.lastIndexOf(12), nums.indexOf(99));
console.log(users.some(u => u.role === 'admin'));
console.log(users.every(u => u.active));
console.log(users.filter(u => u.active).length);
console.log(users.filter(u => u.role === 'owner'));
結果を式ごとに並べると次のとおりです。
| 式 | 結果 |
|---|---|
users.find(u => u.role === 'member') |
{ id: 2, name: 'Baba', role: 'member', active: false } |
users.find(u => u.role === 'owner') |
undefined |
users.findIndex(u => u.role === 'member') |
1 |
users.findIndex(u => u.role === 'owner') |
-1 |
users.findLast(u => u.role === 'member') |
{ id: 3, name: 'Chiba', role: 'member', active: true } |
users.findLastIndex(u => u.role === 'member') |
2 |
nums.includes(12) / nums.includes(99) |
true / false |
nums.indexOf(12) / nums.lastIndexOf(12) |
1 / 3 |
nums.indexOf(99) |
-1 |
users.some(u => u.role === 'admin') |
true |
users.every(u => u.active) |
false |
users.filter(u => u.active).length |
3 |
users.filter(u => u.role === 'owner') |
[] |
見つからないときの戻り値がメソッドごとに違う点が最初のポイントです。要素を返すものは undefined、位置を返すものは -1、真偽値を返すものは false、filter は空配列です。
条件の関数が受け取る3つの引数
find・some・every・filter などに渡す関数は、(要素, 位置, 配列全体) の3つを受け取ります。
['x', 'y'].findIndex((el, i, arr) => { console.log(' ', el, i, arr); return false; });
x 0 [ 'x', 'y' ]
y 1 [ 'x', 'y' ]
位置を使うと「前の要素より大きい最初の要素」のような条件も書けます。
const temps = [18, 17, 19, 22];
console.log(temps.find((t, i, arr) => i > 0 && t > arr[i - 1])); // 19
開始位置を指定する(includes・indexOf の第2引数)
値で探す3つのメソッドは、第2引数で探し始める位置を指定できます。
式(nums は [5, 12, 8, 12, 3]) |
結果 | 意味 |
|---|---|---|
nums.indexOf(12, 2) |
3 |
位置2以降で最初の 12 |
nums.includes(5, 1) |
false |
位置1以降に 5 はない |
nums.includes(3, -1) |
true |
負の数は末尾から数える(最後の1件だけを調べる) |
「2つ目の 12 の位置」を知りたいときは、nums.indexOf(12, nums.indexOf(12) + 1) のように1つ目の次から探します。
途中で打ち切るかを確かめる
find・some・every は、結果が決まった時点で残りを調べません。 条件の関数が何回呼ばれたかを数えて確かめます。配列は [1, 2, 3, 4, 5]、条件は「2と等しい」です。
const a = [1, 2, 3, 4, 5];
const count = (name, run) => {
let c = 0;
run(x => { c++; return x === 2; });
console.log(name.padEnd(14), c, '回');
};
count('find', f => a.find(f));
count('findIndex', f => a.findIndex(f));
count('findLast', f => a.findLast(f));
count('findLastIndex', f => a.findLastIndex(f));
count('some', f => a.some(f));
count('every', f => a.every(f));
count('filter', f => a.filter(f));
find 2 回
findIndex 2 回
findLast 4 回
findLastIndex 4 回
some 2 回
every 1 回
filter 5 回
| メソッド | 呼ばれた回数 | 理由 |
|---|---|---|
| find / findIndex / some | 2回 | 2番目で条件に合ったので終了 |
| findLast / findLastIndex | 4回 | 後ろから 5, 4, 3, 2 と調べて終了 |
| every | 1回 | 最初の 1 が条件に合わず、false が確定して終了 |
| filter | 5回 | 全件を調べる |
「あるかどうか」だけを知りたいのに filter(fn).length > 0 と書くと、見つかったあとも最後まで調べ、さらに配列を1つ作ります。 some(fn) に置き換えると、意図も伝わりやすく処理も少なくなります。
| 直す前 | 直したあと | 得られること |
|---|---|---|
arr.filter(fn).length > 0 |
arr.some(fn) |
見つかった時点で終わる |
arr.filter(fn)[0] |
arr.find(fn) |
見つかった時点で終わる、配列を作らない |
arr.filter(fn).length === arr.length |
arr.every(fn) |
合わないものが出た時点で終わる |
arr.findIndex(fn) !== -1 |
arr.some(fn) |
意図が読み取りやすい |
arr.indexOf(v) !== -1 |
arr.includes(v) |
意図が読み取りやすく、NaN にも対応 |
[...arr].reverse().find(fn) |
arr.findLast(fn) |
コピーと反転が不要 |
実行結果で確かめる注意点
NaN は indexOf で見つからず、includes なら見つかる
const v = [1, NaN, 3];
console.log(v.indexOf(NaN), v.includes(NaN), v.findIndex(x => Number.isNaN(x)), v.lastIndexOf(NaN));
console.log(NaN === NaN, Object.is(NaN, NaN));
-1 true 1 -1
false true
| 式 | 結果 | 理由 |
|---|---|---|
v.indexOf(NaN) |
-1 |
=== と同じ比較を使う。NaN === NaN は false |
v.lastIndexOf(NaN) |
-1 |
同上 |
v.includes(NaN) |
true |
NaN 同士を等しいとみなす比較(SameValueZero)を使う |
v.findIndex(x => Number.isNaN(x)) |
1 |
位置がほしいときはこの形 |
計算結果に NaN が混ざっていないかを調べるなら includes、位置まで知りたいなら findIndex と Number.isNaN の組み合わせを使います。
空配列の every は true、some は false
console.log([].every(x => x > 100), [].some(x => x > 100));
console.log([].find(x => true), [].findIndex(x => true), [].includes(undefined), [].filter(x => true));
true false
undefined -1 false []
every は「条件に合わない要素が1つもない」ときに true を返します。空配列には合わない要素がないので、条件が何であっても true です。
これが効いてくるのは、次のような場面です。
const selected = []; // 何も選ばれていない
console.log('全員OK?', selected.every(u => u.active));
console.log('件数も確認:', selected.length > 0 && selected.every(u => u.active));
全員OK? true
件数も確認: false
「1件以上あり、かつ全部が条件を満たす」ことを確かめたいときは、length の確認を添えます。
| 配列の状態 | every(fn) |
some(fn) |
|---|---|---|
| 空配列 | true | false |
| 全部が条件に合う | true | true |
| 一部だけ合う | false | true |
| 1つも合わない | false | false |
オブジェクトや配列は includes で一致しない
console.log(users.includes({ id: 1, name: 'Aoki', role: 'admin', active: true }));
console.log(users.indexOf({ id: 1 }));
console.log(users.includes(users[0]));
console.log(users.some(u => u.id === 1));
console.log([[1, 2], [3, 4]].includes([1, 2]));
console.log([[1, 2], [3, 4]].some(x => x[0] === 1 && x[1] === 2));
false
-1
true
true
false
true
includes と indexOf は、オブジェクトを「中身」ではなく「同じ実体かどうか(参照)」で比べます。 中身がまったく同じでも、別に作ったオブジェクトは別物として扱われます。users.includes(users[0]) が true なのは、配列に入っている実体そのものを渡しているからです。
| 探したいもの | 書き方 |
|---|---|
| id が一致するオブジェクトがあるか | users.some(u => u.id === 1) |
| id が一致するオブジェクトそのもの | users.find(u => u.id === 1) |
| 複数の項目が一致するか | users.some(u => u.role === 'member' && u.active) |
| 配列の配列から探す | pairs.some(p => p[0] === 1 && p[1] === 2) |
型が違うと一致しない(数値の 2 と文字列の '2')
console.log([1, 2, 3].includes('2'), [1, 2, 3].indexOf('2'), ['1', '2'].includes(2));
console.log([1, 2, 3].includes(Number('2')));
false -1 false
true
入力欄やURLのパラメータ、環境変数から届く値は文字列です。数値の配列から探すときは Number で変換してから渡すと一致します。
if (arr.indexOf(x)) は意図と逆になる
const list = ['a', 'b'];
console.log(list.indexOf('a') ? 'あり' : 'なし');
console.log(list.indexOf('z') ? 'あり' : 'なし');
console.log(list.indexOf('a') !== -1, list.includes('a'));
なし
あり
true true
先頭にある 'a' の位置は 0 で、0 は偽と判定されます。逆に、見つからないときの -1 は真と判定されます。位置がほしいわけでなければ includes を使うのが最も読みやすい書き方です。 indexOf を使う場合は必ず !== -1 と比べます。
find の結果が 0 や undefined のとき
const withUndef = [undefined, 0, ''];
console.log(withUndef.find(x => x === undefined)); // undefined(見つかったのに undefined)
console.log(withUndef.find(x => x === 0)); // 0
console.log(withUndef.findIndex(x => x === 0)); // 1
console.log([0, 1].find(x => x === 0) ? 'あり' : 'なし と判定される');
undefined
0
1
なし と判定される
find は見つかった要素をそのまま返すので、要素が 0・空文字・undefined の場合は「見つからなかった」と区別がつきにくくなります。
| 目的 | 書き方 |
|---|---|
| あるかどうか | arr.some(fn) または arr.includes(値) |
| 見つからないときに既定値を使う | arr.find(fn) ?? 既定値 |
| 見つかったかを確実に判定したい | const i = arr.findIndex(fn); if (i !== -1) { ... arr[i] ... } |
空きのある配列(疎配列)での違い
const sparse = [1, , 3]; // 位置1が空き
console.log(sparse.includes(undefined), sparse.indexOf(undefined));
console.log(sparse.findIndex(x => x === undefined), sparse.some(x => x === undefined));
true -1
1 false
| メソッド | 空きの扱い(実行結果から) |
|---|---|
| includes / find / findIndex | 空きを undefined として調べる |
| indexOf / some | 空きを飛ばす |
空きのある配列はめったに使いませんが、new Array(3) で作った配列などで結果が食い違ったときは、この違いを思い出すと原因に早くたどり着けます。
文字列の配列:大文字小文字と部分一致
console.log(['Apple', 'banana'].includes('apple'));
console.log(['Apple', 'banana'].some(s => s.toLowerCase() === 'apple'));
console.log(['Apple', 'banana'].some(s => s.includes('nan')));
false
true
true
| やりたいこと | 書き方 |
|---|---|
| 完全一致 | arr.includes('Apple') |
| 大文字小文字を区別しない | arr.some(s => s.toLowerCase() === key.toLowerCase()) |
| 部分一致(含む) | arr.some(s => s.includes('nan')) |
| 前方一致 | arr.some(s => s.startsWith('App')) |
| 部分一致するものを全部 | arr.filter(s => s.includes('nan')) |
配列の includes は要素との完全一致、文字列の includes は部分一致です。名前が同じなので、どちらに対して呼んでいるかを意識すると混乱しません。
関数をそのまま渡すときの型
console.log(['1', '2', '3'].find(Number.isInteger), [1, 2, 3].find(Number.isInteger));
undefined 1
Number.isInteger は文字列に対して false を返すので、文字列の配列では何も見つかりません。型をそろえてから探すのが確実です。
async 関数を条件にすると常に「あり」になる
console.log([1, 2, 3].some(async x => x > 100), [1, 2, 3].find(async x => x > 100));
true 1
async 関数は Promise を返し、Promise はオブジェクトなので真と判定されます。some は常に true、find は常に先頭の要素を返します。 非同期の条件で調べるときは、先に全件の結果を待ってから判定します。
const results = await Promise.all([1, 2, 3].map(async x => x > 100));
console.log(results.some(Boolean)); // false
some と every の関係を整理する
some と every は裏表の関係にあります。否定と組み合わせると4通りの問いが書けます。
| 知りたいこと | 書き方 | 同じ意味の別の書き方 |
|---|---|---|
| 1つでも条件に合う | arr.some(fn) |
!arr.every(x => !fn(x)) |
| 全部が条件に合う | arr.every(fn) |
!arr.some(x => !fn(x)) |
| 1つも条件に合わない | !arr.some(fn) |
arr.every(x => !fn(x)) |
| 条件に合わないものが1つはある | !arr.every(fn) |
arr.some(x => !fn(x)) |
入力チェックの例です。
const form = [
{ field: 'name', value: 'Aoki' },
{ field: 'email', value: '' },
{ field: 'tel', value: '000-0000' },
];
const allFilled = form.every(f => f.value !== '');
const firstEmpty = form.find(f => f.value === '');
console.log(allFilled, firstEmpty?.field); // false email
every で全体の合否を出し、find で最初に合わなかった項目を取り出す、という組み合わせはよく使う形です。
配列同士の関係も some / every / includes で書けます。
const required = ['read', 'write'];
const granted = ['read', 'write', 'delete'];
console.log(required.every(p => granted.includes(p))); // すべて含む → true
console.log(required.some(p => granted.includes(p))); // どれか1つでも含む → true
件数が多いときは Set・Map に置き換える
includes や find は先頭から順に調べる
includes・indexOf・find・some は、配列を先頭から1つずつ調べます。配列の件数を n とすると、1回の検索にかかる手間は n に比例します(計算量の表記では O(n))。1回だけなら気になりませんが、ループの中で何度も検索すると「探す回数 × 配列の件数」になります。
Set と Map は、値やキーから直接たどれる仕組み(ハッシュ)を持つので、1回の検索は件数にほぼ左右されません。
| 操作 | 配列 | Set / Map |
|---|---|---|
| 値があるか | arr.includes(v) … 件数に比例 |
set.has(v) … ほぼ一定 |
| キーから要素を得る | arr.find(x => x.id === id) … 件数に比例 |
map.get(id) … ほぼ一定 |
| 準備 | 不要 | 最初に1回 new Set(arr) / new Map(...) を作る |
計測用のコードと結果
10万件の配列に対して1万回検索する時間を計りました。Set・Map は「作る時間」も含めています。
// bench.mjs (node bench.mjs で実行)
const N = 100_000; // 探される側の件数
const Q = 10_000; // 探す回数
const ids = Array.from({ length: N }, (_, i) => i * 2); // 偶数だけ
const rows = ids.map(id => ({ id, name: 'item' + id }));
const queries = Array.from({ length: Q }, (_, i) => (i * 7919) % (N * 2));
function time(label, fn) {
const t0 = performance.now();
const hit = fn();
const ms = performance.now() - t0;
console.log(`${label.padEnd(34)} ${ms.toFixed(1).padStart(8)} ms (hit=${hit})`);
}
for (let round = 1; round <= 2; round++) {
console.log(`--- ${round} 回目`);
time('Array.includes', () => queries.filter(q => ids.includes(q)).length);
time('Array.indexOf', () => queries.filter(q => ids.indexOf(q) !== -1).length);
time('Set を作る + has', () => { const s = new Set(ids); return queries.filter(q => s.has(q)).length; });
time('Array.find (オブジェクト)', () => queries.filter(q => rows.find(r => r.id === q)).length);
time('Array.some (オブジェクト)', () => queries.filter(q => rows.some(r => r.id === q)).length);
time('Map を作る + get', () => { const m = new Map(rows.map(r => [r.id, r])); return queries.filter(q => m.get(q)).length; });
}
Node.js 22.22.2 での結果(2回目の値)です。
| 方法 | 所要時間 | 見つかった件数 |
|---|---|---|
Array.includes |
872.9 ms | 5000 |
Array.indexOf |
1003.8 ms | 5000 |
Set を作る + has |
7.3 ms | 5000 |
Array.find(オブジェクト) |
1461.6 ms | 5000 |
Array.some(オブジェクト) |
1367.0 ms | 5000 |
Map を作る + get |
13.6 ms | 5000 |
1回目はそれぞれ 1260.2 / 1151.2 / 8.2 / 2348.4 / 2143.6 / 26.7 ms でした。数値は実行環境やデータで変わりますが、この条件では Set・Map への置き換えで 100 倍前後の差が出ています。
一方、件数が少ないときはどちらでも差はわずかです。10件の配列を10万回調べた結果です。
| 方法 | 所要時間 |
|---|---|
Array.includes(10件) |
2.4 ms |
Set.has(10件) |
1.8 ms |
置き換えるかどうかの判断表
| 状況 | おすすめ |
|---|---|
| 配列が数十件まで、検索は数回 | 配列のまま includes / find |
| ループの中で同じ配列を何度も検索する | ループの外で Set / Map を1回作る |
| 2つの配列を突き合わせる(重複・差分・結合) | 片方を Set / Map にする |
| id からオブジェクトを何度も引く | new Map(rows.map(r => [r.id, r])) |
| 条件が毎回違う(範囲、部分一致など) | 配列のまま filter / find |
置き換えの書き方
// 許可された role だけ残す
const allow = new Set(['admin', 'member']);
console.log(users.filter(u => allow.has(u.role)).map(u => u.name));
// id から引けるようにする
const byId = new Map(users.map(u => [u.id, u]));
console.log(byId.get(3).name, byId.get(99), byId.has(2));
[ 'Aoki', 'Baba', 'Chiba' ]
Chiba undefined true
| 直す前 | 直したあと |
|---|---|
orders.map(o => users.find(u => u.id === o.userId)) |
const byId = new Map(users.map(u => [u.id, u])); を先に作り orders.map(o => byId.get(o.userId)) |
a.filter(x => b.includes(x)) |
const bs = new Set(b); を先に作り a.filter(x => bs.has(x)) |
a.filter(x => !b.includes(x)) |
const bs = new Set(b); を先に作り a.filter(x => !bs.has(x)) |
Set でも比較のしかたは includes と同じです。NaN は見つかり、オブジェクトは参照で比べます。
console.log(new Set([1, NaN]).has(NaN), new Set([{ id: 1 }]).has({ id: 1 }));
true false
オブジェクトを Set・Map で探したいときは、id などの基本の値をキーにするのがポイントです。
findLast と findLastIndex の使いどころ
findLast・findLastIndex は、配列を後ろから調べます。時系列に並んだデータから「最新の1件」を取り出すときに向いています。
const logs = [
{ at: '09:00', level: 'info', msg: '起動' },
{ at: '09:05', level: 'error', msg: '接続できません' },
{ at: '09:10', level: 'info', msg: '再接続' },
{ at: '09:12', level: 'error', msg: '応答なし' },
];
console.log(logs.findLast(l => l.level === 'error').msg); // 応答なし
console.log(logs.findLastIndex(l => l.level === 'error')); // 3
以前は [...logs].reverse().find(...) のようにコピーして反転する書き方が使われていましたが、findLast なら配列を作らず、後ろから見つかった時点で終わります。
対応バージョンは次のとおりです。MDN の互換性データ(npm パッケージ @mdn/browser-compat-data 8.1.4)を今回取得して確認しました。
| メソッド | Chrome | Edge | Firefox | Safari | Node.js |
|---|---|---|---|---|---|
find / findIndex |
45 | 12 | 25 | 8 | 4.0.0 |
includes |
47 | 14 | 43 | 9 | 6.0.0 |
findLast / findLastIndex |
97 | 97 | 104 | 15.4 | 18.0.0 |
(findIndex は find と同じ行にまとめていますが、今回データを取得して確かめたのは find・includes・findLast・findLastIndex の4つです。)
よく使う検索パターン集
そのまま貼って使える形でまとめます。データは冒頭の users と nums です。
| やりたいこと | コード |
|---|---|
| id で1件取り出す | users.find(u => u.id === 3) |
| 見つからないときは既定値 | users.find(u => u.id === 99) ?? null |
| 見つかった要素の項目を安全に読む | users.find(u => u.id === 99)?.name |
| 条件に合う要素の位置を得て書き換える | const i = users.findIndex(u => u.id === 2); if (i !== -1) users[i].active = true; |
| 条件に合う要素を取り除いた配列 | users.filter(u => u.id !== 2) |
| 複数の値のどれかに一致 | ['admin', 'member'].includes(user.role) |
| 複数条件(かつ) | users.filter(u => u.role === 'member' && u.active) |
| 複数条件(または) | users.filter(u => u.role === 'admin' || u.role === 'guest') |
| 重複があるか | new Set(nums).size !== nums.length |
| 最大の要素を持つオブジェクト | users.reduce((a, b) => (a.id > b.id ? a : b)) |
| 条件に合う件数 | users.filter(u => u.active).length |
| 最後の要素 | users.at(-1) |
「複数の値のどれかに一致」は、role === 'admin' || role === 'member' と並べる代わりに includes を使うと、値が増えても1か所の修正で済みます。
const editable = ['admin', 'member'];
if (editable.includes(user.role)) {
// 編集できる
}
つまずきやすい点の一覧
| 症状 | 原因 | 対応 |
|---|---|---|
| オブジェクトを includes で探しても false | 参照で比べている | some(x => x.id === id) |
| NaN が indexOf で見つからない | === と同じ比較 |
includes(NaN) か findIndex(Number.isNaN) |
| 空配列で every が true になる | 仕様どおり | arr.length > 0 && を添える |
if (arr.indexOf(x)) が逆になる |
位置 0 は偽、-1 は真 | includes(x) か !== -1 |
| find の結果を if で判定したら見つからない扱い | 要素が 0・空文字・undefined | some か findIndex で判定 |
| 数値の配列から文字列で探して見つからない | 型が違う | Number() で変換して渡す |
find(fn).name で TypeError |
見つからず undefined | find(fn)?.name |
| some が常に true | 条件が async 関数 | Promise.all で先に結果を待つ |
filter のあとの [0] |
全件を調べている | find(fn) |
| ループの中の includes が重い | 探す回数 × 件数になっている | Set / Map を先に作る |
よくある質問
Q. includes と indexOf はどちらを使えばよいですか?
A. あるかどうかを知りたいなら includes、位置が必要なら indexOf です。includes は true / false を返すので if にそのまま書け、NaN も見つけられます。indexOf は見つからないときに -1 を返すので、!== -1 との比較が必要です。
Q. some と includes はどう違いますか? A. includes は「この値と等しい要素があるか」、some は「この条件に合う要素があるか」です。数値や文字列をそのまま探すなら includes、オブジェクトの項目で探す・範囲で探すといった条件つきなら some を使います。
Q. find と filter はどう違いますか? A. find は最初に合った1件を返して終了し、見つからなければ undefined です。filter は全件を調べて、合ったものすべてを配列で返します(なければ空配列)。1件だけほしいなら find、全部ほしいなら filter です。
Q. 配列の検索は遅いのですか? A. 数十件から数百件を数回調べる程度なら、配列のままで十分です。本記事の計測では、10件の配列では includes と Set.has の差はわずかでした。10万件に対して1万回のように「件数 × 回数」が大きくなる場合に、Set・Map への置き換えが効いてきます。
まとめ
- 「あるか」は includes / some、「要素」は find、「位置」は indexOf / findIndex、「全部」は filter と、ほしいものでメソッドが決まります。
- 見つからないときの戻り値は、要素なら undefined、位置なら -1、真偽なら false、filter は空配列です。
- find・some・every は結果が決まった時点で打ち切り、filter は最後まで調べます。存在チェックに
filter().lengthを使っていたら some に置き換えます。 - NaN は indexOf では見つからず、includes なら見つかります。
- 空配列の every は true です。1件以上を前提にするなら length の確認を添えます。
- オブジェクトは includes では一致しないので、some や find で項目を比べます。
- 同じ配列を何度も検索するなら Set・Map を先に作ると、今回の計測では 100 倍前後の差が出ました。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「JavaScript配列の検索|find・includes・some・every」
株式会社RJCでは、JavaScriptの配列検索メソッドの使い分けを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。