永遠に機能する:強力な削減


著者によってFederico Kereki
2009年12月に、ES 5(正式に:)ECMA-262 5th Edition ) いくつかの新しい機能と方法が含まれています.最も重要なものは、機能的プログラミング(FP)の観点から、トリオ(カルテット)であった-

  • .map(...) この関数は、それぞれの要素に対して与えられた関数を

  • .filter(...) 指定した条件を満たす配列要素を選択します
  • そして、ここで勉強する強力な方法の2つのバージョン.reduce(...) and .reduceRight(...) . これらの関数は、すべての配列(左から右、右から左へ)に操作を適用します.
  • これらはすべて、私たちが既に見たようにForever Functional: Higher Order Functions -- Functions to rule functions 記事.ここで定義されているように、Hofは1つ以上の関数を引数として扱う関数です.上記のすべてのメソッドがこれを満たし、それらを使用すると、より宣言的な方法でコード化できます.たとえば、配列から要素を選択するには、ループを書き出したり、新しい出力配列を初期化する必要はありませんyourArray.filter(someFunction) そして、新しい配列は、ときにtrueを生成する元の配列からすべての値で生成されますsomeFunction(...) が適用されます.どのように結果を生成するかを指定するのではなく、段階的に、どの要素があなたに興味を持っているかを宣言し、このFP指向メソッドがあなたのために作業を行います.
    興味深いのは、これらのメソッドのほとんどが実際に冗長であることです.実際には、1つだけで行うことができます..reduce(...) ! 本稿では、その兄弟をエミュレートするためにどのように使用するかを研究します.それは私たちに彼らの内部の仕事に対する洞察を与えます、そして、我々は我々がどこかほかで使用したいかもしれない技術も見ます.最初にどのようにリマインダーを取得しましょう.reduce(...) そして、他のメソッドに代わるものとしてそれを使用するようになります.

    配列を値に減らす
    通常使用する方法.reduce(...) のようですyourArray.reduce(someFunction, initialValue) . あなたは初期値を提供することをスキップすることができます、しかし、それは線の下でエラーにつながるかもしれません、それで、よりよくありません.そのプロセスは以下の通りです.
  • 初期値を持つアキュムレータを初期化する
  • 集合アキュムレータ= somefunction (アキュムレータ、YourArray [ 0 ] )
  • 次にアキュムレータ= somefunction (アキュムレータ、YourArray [ 1 ] ]を設定します.
  • それからアキュムレータ= somefunction (アキュムレータ、YourArray [ 2 ] )
  • ...すべての配列を順番に移動し、最後の要素まで同じです.
  • 非常によく知られた例:あなたが数の配列を持っているならば、あなたは書くことによって一行でそれらを加えることができましたyourArray.reduce((x,y)=>x+y, 0) . アキュムレータはゼロ(論理和)で始まる.それから、配列の各々の連続した要素はそれに加えられるでしょう.最終結果は、配列のすべての要素の合計です.このように働くことは、すべての可能性を回避する"off-by-one" errors , ハンドルの初期化と結果の処理、および副作用は無料です.我々は、副作用のない、純粋な機能を議論したForever Functional: Injecting for Purity 記事

    In usual FP-speak, .reduce(...) is known as "fold" or possibly "foldl" ("fold left") and .reduceRight(...) is "foldr" ("fold right").


    今、それは明らかではないかもしれない.reduce(...) 他のすべてのメソッドを模倣するのに十分です.では、最も簡単な場合から始めましょう.

    右からの減少
    兄弟姉妹.reduce(...) , .reduceRight(...) 正確に同じように動作しますが、最初から最後まで配列を通過するのではなく、終わりから始まり、先頭に戻ります.多くのもの(上記のように配列を合計するような)のために、これは違いを全くしません、しかし、他の場合では、それはそうするかもしれません.どうやってこのメソッドをエミュレートできますか?それは簡単になります!
    唯一の違いは、私たちが言ったように、配列が右から左の順序で処理されるということです.それで、最も簡単な解決策は、(最初に)配列を逆にして、(2)値にそれを減らすことです!私たちはほとんど「方程式」を書くことができました.yourArray.reduceRight(someFunction, initialValue) === yourArray.reverse().reduce(someFunction, initialValue) ... しかし、これには問題がありますあなたはそれを見ることができますか?
    何が起こっているのですか.その鍵は.reverse(...) Mutatorメソッドです.実際には配列を反転します.それで、このように働いて、我々は同じ最終結果を得ます.しかし、我々は予想外の副作用も生じました:配列は逆にされます!正しい方法は、最初に配列のコピーを生成することです.書くべきだ
    yourArray.reduceRight(someFunction, initialValue) === 
    [...yourArray].reverse().reduce(someFunction, initialValue)
    
    を、なぜ私たちは、このバージョンを使用しますか?簡単な答え:我々はありません!我々が欲しかった結論は.reduce(...) 代替のエミュレート自体が可能です.reduceRight(...) ... しかし、この場合、解決は問題の価値がないようです!私たちが言ったように、これは横方向の思考の運動のより多くのものであり、物事を行う新しい方法を見つける、そしてそれは目的地よりむしろ方法についての詳細です.他の方法について見ましょう.

    配列のマッピング
    要素のリストを通過し、いくつかの種類の変換を実行するアルゴリズムの非常に一般的なパターンです.ときにどのようにプログラムを学ぶために起動すると、そのようなループを書くの基本的なタスクです.私たちは確かに、そのファッションの配列を処理することができますfor(...) しかし、.map(...) 私たちは、より宣言的な方法で機能的に働くことができます.一般的な例:人の配列でfirstName and lastName すべてのフルネームを生成できます.
    let fullNames = persons.map((p) => p.firstName + " " + p.lastName);
    
    書くときyourArray.map(aFunction) その結果はaFunction(...) (マッピング関数)を配列の各要素に適用し、新しい配列を生成します.

    In math, a "map" is a transformation of values from a set into another one, like strings to numbers, objects to booleans, etc. In JavaScript, .map(...) transforms an array into a new one.


    このプロセスをシミュレートできますか.reduce(...) ? 答えは“はい”、ソリューションは短いです.新しい配列を作成したい場合は、空の配列を初期値として縮小し、マップされた結果を一度に追加します.
    const mapByReduce = (yourArray, aFunction) => yourArray.reduce((a, v) => a.concat(aFunction(v)), []);
    
    コードを読みましょう.空の配列から始めます.各要素v 配列の関数に引数として与えられ、結果が配列に連結されるa . 終了すると、生成された配列が適用されるのと同じ結果になります.map(...) ――もう一度――.reduce(...) その力を証明!私たちはmapByReduce(...) 関数として、我々はそれに追加することができますArray.prototype 我々が欲しかったならば-しかし、なぜこれをしますか?我々は新しい実装を研究していくつかの方法についての理解を深めるために勉強しています.最後の方法を考えましょう.

    オープンソースセッション
    Webアプリケーションの生産におけるデバッグは、挑戦的で、時間がかかるかもしれません.OpenReplay フルストーリー、ログオンとhotjarにオープンソースの代替手段です.それはあなたが監視し、すべてのユーザーが再生し、どのようにすべての問題のためにあなたのアプリの行動を示す再生することができます.
    それはあなたのユーザーの肩を見ながら、ブラウザの検査官を開いているようなものだ.
    OpenReplayは現在利用可能なオープンソースの代替手段です.

    ハッピーデバッギング、現代のフロントエンドチームStart monitoring your web app for free .

    配列のフィルタリング
    配列のフィルター処理は、配列の各要素に対して関数が適用されるようにマッピングするのといくぶん似ていますが、この場合、それは異なって使用されますtruthy 値は、対応する要素が選択され、結果に追加されますfalsy 例えば、要素は除外されます.例えば、前の例のように、同じ配列の場合、それぞれがage 属性は、簡単に大人を選ぶことができます.
    let adults = persons.filter((p) => p.age >= 21);
    
    どうやってこれをエミュレートできますか?解決法はマッピングのそれと似ている.空の配列から始め、各要素にフィルタリング機能を適用した後、関数が真の値を生成した場合のみ、結果に追加します.
    const filterByReduce = (yourArray, aFunction) => yourArray.reduce((a, v) => (aFunction(v) ? a.concat(v) : a), []);
    
    関数の結果によって、配列の元の要素を最終結果に追加するかどうかを決定するために、三項演算子を使用します.もう一度、我々はそれを見た.reduce(...) あなたが唯一のツールとしてそれを使用することができますので、十分に強力です-あなたは、明らかに、他の方法が既に存在するので、したくない!

    概要
    この記事では、いくつかの基本的なFPメソッドを考えました.しかし、これは単なる運動です.なぜ車輪を再発明するのですか?詳細は、私たちはいくつかの興味深いテクニックを適用して、オリジナルの方法で削減を使用する方法についての詳細を学びました.