1件なら find()、複数なら filter()、あるかどうかなら some()。大文字小文字や全角半角で見つからない理由、複数の項目をまたいで探す形、何度も引くなら Map にする判断まで確かめます。
答え
欲しいものが何かで、使うものが決まります。
example.js
const users = [
{ id: 1, name: '田中 太郎' },
{ id: 2, name: '佐藤 花子' },
];
console.log(users.find((user) => user.id === 2));
console.log(users.filter((user) => user.name.includes('田')));
console.log(users.some((user) => user.id === 1));
出力書き換えて実行できます
{ id: 2, name: '佐藤 花子' }
[ { id: 1, name: '田中 太郎' } ]
true
javascript
const users = [{ id: 1, name: '田中' }];
const user = users.find((u) => u.id === 99);
console.log(user.name);
TypeError: Cannot read properties of undefined (reading 'name')
| 欲しいもの |
使うもの |
見つからないとき |
| 1件(要素そのもの) |
find() |
undefined |
| 全部(配列) |
filter() |
[] |
| あるかどうか |
some() |
false |
| 何番目か |
findIndex() |
-1 |
includes() は使えません。 中身の条件で探すなら some() です。
→ every() と some()
見つからないときに必ず備える
example.js
const users = [{ id: 1, name: '田中' }];
const user = users.find((u) => u.id === 99);
console.log(user?.name ?? '(見つかりません)');
出力書き換えて実行できます
(見つかりません)
javascript
const users = [{ email: 'TANAKA@example.com' }];
console.log(users.filter((user) => user.email.includes('tanaka')));
[]
find() は空振りすると undefined を返します。
その場で受け止めてください。
example.js
const users = [{ email: 'TANAKA@example.com' }];
const keyword = 'tanaka';
console.log(
users.filter((user) => user.email.toLowerCase().includes(keyword.toLowerCase())),
);
出力書き換えて実行できます
[ { email: 'TANAKA@example.com' } ]
example.js
const decomposed = 'が';
const composed = 'が';
console.log(decomposed.length, composed.length);
console.log(decomposed === composed);
console.log([...decomposed].map((c) => c.codePointAt(0).toString(16)));
console.log(decomposed.normalize('NFC') === composed);
出力書き換えて実行できます
2 1
false
[ '304b', '3099' ]
true
→ ?. と ??
大文字小文字で見つからない
example.js
console.log('タナカ'.normalize('NFKC'));
console.log('AB'.normalize('NFKC'));
出力書き換えて実行できます
タナカ
AB
example.js
const normalize = (text) => text.normalize('NFKC').toLowerCase();
const users = [{ name: 'タナカ', email: 'TANAKA@example.com' }];
const keyword = normalize('たなか');
console.log(
users
.map((user) => ({ ...user, searchKey: normalize(user.name + user.email) }))
.filter((user) => user.searchKey.includes(normalize('Tanaka'))),
);
出力書き換えて実行できます
[ { name: 'タナカ', email: 'TANAKA@example.com', searchKey: 'タナカtanaka@example.com' } ]
入っているのに、空です。 includes() は1文字ずつそのまま比べます。
両方を同じ形に揃えてから比べてください。
example.js
const users = [
{ name: '田中', email: 'a@example.com', tel: '090' },
{ name: '佐藤', email: 'b@example.com', tel: '080' },
];
const keyword = '090';
console.log(
users.filter((user) =>
['name', 'email', 'tel'].some((key) => String(user[key]).includes(keyword)),
),
);
出力書き換えて実行できます
[ { name: '田中', email: 'a@example.com', tel: '090' } ]
example.js
const users = [
{ name: '田中', email: 'a@example.com' },
{ name: '佐藤', email: 'b@example.com' },
];
console.log(
users.filter((user) => Object.values(user).some((value) => String(value).includes('佐'))),
);
出力書き換えて実行できます
[ { name: '佐藤', email: 'b@example.com' } ]
入力側だけ小さくしても意味がありません。 両方を揃えます。
全角・半角・濁点で見つからない
日本語では、見た目が同じでも別の文字であることがあります。
example.js
const users = [
{ id: 1, name: 'a' },
{ id: 2, name: 'b' },
];
const byId = new Map(users.map((user) => [user.id, user]));
console.log(byId.get(2));
console.log(byId.get(99));
出力書き換えて実行できます
{ id: 2, name: 'b' }
undefined
半角カナや全角英数も、normalize('NFKC') で揃います。
検索用の値を、あらかじめ1つ作っておくのが確実です。
ひらがなとカタカナは normalize() では揃いません。「たなか」で「タナカ」を出したいなら、別に変換が要ります。この例でも keyword は使わず、'Tanaka' で当てています。
→ 文字を正しく数える
複数の項目をまたいで探す
探す項目を配列に書くと、増減が1か所で済みます。
「どの項目でもよい」なら Object.values() です。
内部用の項目まで検索に含まれます。 何を探すかは、明示するほうが安全です。
何度も引くなら Map にする
find() は毎回、先頭から順に見ます。
同じ配列を何度も引くなら、先に索引を作ってください。
| 引く回数 |
使うもの |
| 1回だけ |
find() |
| 何度も引く |
Map を作る |
Map なら、件数が増えても引く速さが変わりません。
作るのに1回分の走査が要ります。1回しか引かないなら find() のほうが速く、短く書けます。
→ Map と Set
まとめ
- 1件なら
find()、全部なら filter()、有無なら some()
find() の空振りは undefined。 その場で受け止める
- 大文字小文字は両方を揃えてから比べる
- 日本語は
normalize('NFKC') で揃える。ひらがなとカタカナは揃わない
- 複数の項目をまたぐなら、探す項目を配列で明示する
- 何度も引くならMap で索引を作る