🥿
flatMapはfilterを兼ねることができるけど遅い
※頂いたコメントについて追記があります(2023/11/20)
最近、自社のコードレビューで次のような記述を目にしました。
const result = radomNumber.flatMap((item) => {
if (item > 5) {
return item * 2;
}
return [];
});
flatMapは空配列をflatにする際に省けるのでfilterの処理をついでに書けるという事です。つまり、次のコードと返却値は同じです。
const result = radomNumber
.filter((item) => item > 5)
.map((item) => item * 2);
flatMapの方が早くなる可能性があるのかな?(flat()の処理がループより早い可能性)と思いまして計測してみました。
次が計測用のコードです。
const radomNumber = Array.from({ length: 10000000 }, () =>
Math.floor(Math.random() * 10)
);
const f1 = () => {
const start = performance.now();
const result = radomNumber
.filter((item) => item > 5)
.map((item) => item * 2);
const end = performance.now();
console.log("f1 time", end - start);
};
const f2 = () => {
const start = performance.now();
const result = radomNumber.flatMap((item) => {
if (item > 5) {
return item * 2;
}
return [];
});
const end = performance.now();
console.log("f2 time", end - start);
};
ブラウザ上でそのまま実行しました。
結果は
f1 time 177
f2 time 520
よく考えたらflatMapは配列の除去がないため、計算量が常にflatMap≧filter&mapとなり遅くなることに気づきました。
※flatMapは常に2N回、filter&mapは最小でN回。
flatMapのユースケース(追記: 2023/11/20)
コメントで頂いた指摘の通り、typescriptだと配列にnull,undefinedを含む場合、is possibly 'undefined'となるため、filter&mapでそのまま書けないという問題があります(型アサーションする必要がある)。
こういう時はflatMapを使うのはアリだと思いました
(昔、この場面に出くわしたときは元配列の作られ方の見直しで回避した記憶があります)。
ちなみに遅いといっても計測の通り、明確な遅延は実行環境によるといえど、数100万件とか超えるあたりで発生するのでWebアプリでこの点を考慮する必要はほとんどないと思います(ユーザーのリクエスト&レスポンスにその件数の処理が走ることは避けるべき)。
結論
可読性の問題もあるので、普通にfilter&mapで書くべきだなと思いました。
可読性の問題はあるので、nullishな問題以外でflatMapでfilterを兼ねるのはよそうと思いました。
Discussion
小ネタですが、
FlatMap
はundefined
やnull
を除去できるといったこともできます。filter
を使った場合as
とかで型を変更することはできますが、コードが間違っていた場合エラーを起こすことがあるのでflatMap
が役に立ちます。Type Guardを使えば
filter
でも同じことができますが、配列に複数の型がある場合((string | number | undefined)[]
)はflatMap
だと簡潔に書けます。他の方法
どちらにせよ、TSで書くならこの文脈で
flatMap
は使わなくて良さそうですね。ですが、JSで書く際はエディター(VS Code等)の補完が欲しい時はもしかしたらflatMap
が役に立つかもしれませんね。TS Playground
コメントありがとうございます。追記しました。
ちょっと気になったんですが、配列の生成コストってかかってないでしょうか? VM が最適化してくれてるかもしれませんが…
もし配列リテラルを外出しして使い回すとどうなるのか気になります
こういうことですよね?
10回くらい見た感じわずかに(1,2%)早くなってはいました。
支配的ではなさそうですね!ありがとうございます