LINDORのAdvent Calendar(本物)の4日目を開けたところ。
配列とかおれおれAdvent Calendar2018 – 04日目

その名の通り find() で見つける (find) ことができます。

オブジェクトの配列相手に使うのが普通。

基本的な使い方

引数に真偽値を返す関数を与えます。

const users = [
  { id: '101', name: 'Alice' },
  { id: '102', name: 'Bob' },
  { id: '103', name: 'Charlie' },
];

const id = '102';
const targetUser = users.find((v) => v.id === id);
console.log(targetUser);
// => { id: '102', name: 'Bob' }

この例だと (v) => v.name === id の部分が find() の第1引数「真偽値を返す関数」です。だいたいアロー関数で書きます。

このうちさらに v.name === id の部分が真偽値、つまり true か false かになります。数値なら > なんかで比較するのも良いですし、いろいろ確認しなくちゃいけない場合は別の関数を呼ぶようにしても良いです。

使い方の例

完全に合致するもの

あんまり意味がないですね。

const arr = [1, 2, 3];
const result = arr.find((v) => v === 2);
console.log(result); // => 2

含むかどうか、だけを知りたい場合は some() が便利です。別項参照。

ID で検索

これはよくやります。

const arr = [
  { id: 1, name: 'Apple' },
  { id: 2, name: 'Banana' },
  { id: 3, name: 'Orange' },
];
const result = arr.find((v) => v.id === 2);
console.log(result); // => { id: 2, name: 'Banana' }

const result2 = arr.find((v) => v.id === 3);
console.log(result2); // => { id: 3, name: 'Orange' }

フラグ

最初から真偽値の場合は比較演算する必要ないですね。

const arr = [
  { id: 1, name: 'Apple', active: false },
  { id: 2, name: 'Banana', active: true },
  { id: 3, name: 'Orange', active: true },
];
const result = arr.find((v) => v.active);
console.log(result); // => { id: 2, name: 'Banana', active: true }

該当する項目が複数ある場合でも、先頭から探して最初に見つけたものだけが返る点に注意。全て欲しい場合は filter() を使います。別項参照。

他の情報を参照とか、複雑な条件

長い場合は波括弧 {} を付けて明示的に return します。

const items = [
  { id: 1, stockId: 'apple', price: 100 },
  { id: 2, stockId: 'banana', price: 100 },
  { id: 3, stockId: 'orange', price: 100 },
];
const stocks = new Map([
  ['apple', { amount: 24, available: false }],
  ['banana', { amount: 0, available: true }],
  ['orange', { amount: 12, available: true }],
]);

const result = items.find((item) => {
  const stock = stocks.get(item.stockId);
  if (!stock) {
    return false;
  }

  return stock.amount > 0 && stock.available;
});
console.log(result); // => { id: 3, stockId: 'orange', price: 100 }

オブジェクトから探す

範囲外な感じもするけど一応。

オブジェクトには find() がないので、オブジェクトを配列へ一度変換します。変換にはたぶん Object.entries() が良い。

const obj = {
  XWMNNjghEeK: { name: 'apple', price: 80 },
  zqiyjo2RgeZ: { name: 'banana', price: 120 },
  DbMeA6uqMxP: { name: 'orange', price: 1800 },
};

// オブジェクト→配列
const arr = Object.entries(obj);

// 探す
const result = arr.find(([id, data]) => data.name === 'banana');
console.log(result); // => ['zqiyjo2RgeZ', { name: 'banana', price: 120 }]

Object.entries() を使うとオブジェクトをキーと値の配列の配列へ変換できます。説明より試してみてもらった方がわかりやすいと思う。(この例は読みやすいように行を分けたけど、変数 arr を用意せず直接 Object.entries().find() してもいいですよ。)

find() のところで引数を v じゃなくて [id, data] としてるのは、id = v[0] みたいな感じです。詳しくは別記事を(ちょっと違うけど)。

仕様

配列のこれ系のメソッドと同じです。

find() は引数に、関数オブジェクトをひとつ受け取ります。 その関数が最初に true (ないし truthy なもの)を返した際の要素を返します。

与える関数は、3つの引数が与えられます。 また前述のように boolean 値を返してください。

  • value … 配列の要素
  • index … インデックス
  • array … 操作中の配列本体

戻り値

条件に合致した要素があればそれを返します。

なければ undefined です。

細かいところ

  • 先頭から順に呼ばれる
  • 繰り返しの途中で配列の要素が変更されると、変更後の値で呼ばれる
  • 繰り返しの途中で配列の要素数が変更されても、元の回数だけ呼ばれる。削除された要素は undefined として呼ばれる
  • 第2引数にオブジェクトを与えると、コールバック関数の this として利用できる(もちろんこれはアロー関数には影響しない)
  • 22.1.3.8 Array.prototype.find ( predicate [ , thisArg ] )

その他のメソッド

「ひとつ見つける」以外の用途に合うメソッドがあるので、それぞれ使い分けよう。

全部探すなら filter()

条件に合うひとつじゃなくて全部を配列で返してくれます。 見つからなかったら空の配列。

const targetUsers = users.filter((v) => v.active);
console.log(targetUsers.length);

位置を探すなら findIndex()

削除したいとかそういう目的でインデックスが欲しい場合は、 findIndex() というのもあります。

const users = [
  { id: '101', name: 'Alice' },
  { id: '102', name: 'Bob' },
  { id: '103', name: 'Charlie' },
];

const id = '102';
const index = users.findIndex((v) => v.id === id);
console.log(index); // => 2
console.log(users[index]); // => { id: '102', name: 'Bob' }

削除は splice() で。

見つからなかったら -1 になります。

あるかどうかだけわかれば良いなら some()

const existing = users.some((v) => v.id === targetId);
if (existing) {
  console.log('あったよ。');
}
else {
  console.log('なかったよ。');
}

何度も何度も探すなら Map

検索回数が非常に多い場合、配列じゃなくて Map を使うと速いはずです。「何それ?」なら無理に使う必要はないけど。

あくまで「非常に多い」場合で、例えば秒間 10,000 回とか。もっとかな? 普通は配列なら配列のままで問題ないはずです。(データ構造の問題はあるけど、そこらへんの問題意識持てるひとならちゃんと選択できるはず。)

おしまい

find() は割とよく使うので覚えておいて損はないです。

豆知識: 英語の find

「見つける」という意味です。

「探す」なら look for xxx 。

参考

更新履歴

  • 2020-05-07
    • 使い方の例を追加
    • Map の話を追加
  • 2018-12-44
    • 初版