71
55

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaScriptで配列のfilterにasync関数を使いたい

Last updated at Posted at 2019-06-23

表題の通りなんですが、JavaScriptで配列のfilterにasync関数を使う方法を調べていたところ、StackOverflowでカッコいい書き方を見つけたので紹介します。

いつ必要なのか?

次のコードのように、Array.prototype.filter(callback) を使えば配列から特定の条件を満たす要素だけを抜き出すことができます。

const arr = [1,2,3,4,5,6];
console.log(arr.filter(n => n % 2 == 0)); // [2,4,6]

このコールバック関数は配列の要素を受け取ってbooleanを返すような関数である必要があります。

ところが、このコールバックとしてasync関数を書きたいことがあります。つまり、返り値が Promise<boolean> である関数でfilterしたいというわけです。

そんなことあるのかって言われると困るんですが、先ほど人生で初めて必要になりました。

スマートな解

私が良いと思った実装は次のようなものです(注:当初より随分シンプルになりました)。

async function asyncFilter(array, asyncCallback) {
  const bits = await Promise.all(array.map(asyncCallback));
  return array.filter((_, i) => bits[i]);
}

この実装について解説します。

コールバック関数がasync関数なので、処理したい配列にいきなりfilter()を適用するわけにいきません。そこでまずmap()を適用してPromiseの配列にします。そしてPromise.all()でbooleanの配列のPromiseにして、awaitで受ければPromiseがresolveされてbooleanの配列が得られます。

このbooleanの配列をfilter()のコールバック関数で利用します。コールバック関数の第2引数は配列のindexなので、これを使って対応する真偽値を取り出します。(コメントいただいたttatsfさん、41semicolonさん、ありがとうございました!)

利用例

Puppeteerpage.$$()で取り出した要素のうち、viewport内に見えている要素だけを取り出したい場合、下記のように書けます。

const errorBlocks = await my.page.$$('div.error').then(
  els => asyncFilter(els, el => el.isIntersectingViewport())
);

なかなか実用的ですね!

参考URL

71
55
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
71
55

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?