この答えを書くことに触発されて、私は後でこれを注意深く詳細に説明するブログ投稿を拡大して書くことになりました。この問題の考え方をより深く理解したい場合は、チェックすることをお勧めします。私はそれを1つずつ説明し、最後にJSperfの比較を行い、速度の考慮事項について説明します。
とは言うもののArray.reduce()
、tl; drは次のとおりです。要求していること(1つの関数呼び出し内でのフィルタリングとマッピング)を実現するには、を使用します。
しかし、より読みやすい と(あまり重要なこと)は通常かなり速く2のアプローチがあるだけで、使用フィルタとマップには、互いに連鎖:
[1,2,3].filter(num => num > 2).map(num => num * 2)
以下は、どのようにArray.reduce()
機能するか、および1回の反復でフィルターとマップを実行するためにどのように使用できるかについての説明です。繰り返しになりますが、これがあまりにも凝縮されている場合は、上記のリンク先のブログ投稿を参照することを強くお勧めします。これは、明確な例と進行状況を備えた、はるかにわかりやすいイントロです。
(通常は無名の)関数である引数をreduceに与えます。
その無名関数は2つのパラメーターを取ります。1つ(map / filter / forEachに渡される無名関数など)は、操作対象の反復子です。減らすために渡される無名関数には別の引数がありますが、これらの関数は受け入れません。これは、関数呼び出し間で渡される値であり、メモと呼ばれることがよくあります。
Array.filter()は1つの引数(関数)のみを取りますが、Array.reduce()は重要な(オプションですが)2番目の引数も取ります:その匿名関数にその匿名関数として渡される 'memo'の初期値最初の引数。その後、変更して関数呼び出し間で渡すことができます。(指定されていない場合、最初の無名関数呼び出しの「memo」がデフォルトで最初のiterateeになり、「iteratee」引数が実際には配列の2番目の値になります)
この例では、空の配列を渡して開始し、関数に基づいて反復子を配列に挿入するかどうかを選択します。これがフィルタリングプロセスです。
最後に、匿名関数呼び出しごとに「進行中の配列」を返し、reduceはその戻り値を受け取り、それを引数(メモと呼ばれる)として次の関数呼び出しに渡します。
これにより、フィルターとマップを1回の反復で実行でき、必要な反復回数を半分に減らすことができます。ただし、各反復で2倍の作業を行うだけなので、JavaScriptでそれほど高価ではない関数呼び出し以外は実際には何も保存されません。 。
より完全な説明については、MDNドキュメント(またはこの回答の冒頭で参照されている私の投稿)を参照してください。
Reduce呼び出しの基本的な例:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
if (iteratee > 1) {
memo.push(iteratee * 2);
}
return memo;
}, initialMemo)
console.log(array)
より簡潔なバージョン:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
最初の反復は1以下であり、フィルタリングされていることに注意してください。また、その存在を明確にし、注意を引くためだけに名前が付けられたinitialMemoにも注意してください。この場合も、最初の無名関数呼び出しに「memo」として渡され、次に無名関数の戻り値が「memo」引数として次の関数に渡されます。
メモの典型的なユースケースのもう1つの例は、配列内の最小または最大の数値を返すことです。例:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
独自のreduce関数を作成する方法の例(これは、このような関数を理解するのに役立つことがよくあります):
test_arr = [];
test_arr.my_reducer = function(reduceFunc, initialMemo) {
const initialMemoIsIndexZero = arguments.length < 2;
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
memo = reduceFunc(memo, this[i]);
}
return memo;
}
実際の実装では、たとえばインデックスなどにアクセスできますが、これがその要点を単純に感じるのに役立つことを願っています。