JS配列法を解明するすべての記事がなぜゴミであるか


本来のポストhttps://siderite.dev/blog/why-all-articles-about-demystifying-js-array-metho/
毎月、あるいは、私はいくつかのdevによって投稿された別の記事を参照してください.通常、“Demystified”や“理解”や“N配列メソッドを使用して”または“JavaScriptを簡素化”または何か似たような単語を使用してキャッチーなタイトルで投稿されます.それはとても世俗的で退屈になったので、誰かがまだこれらの疲れた考えをキャッシュにしようとしているように見えます.だからそれを停止!しかし、それはより悪くなります.これらの記事は、JavaScriptがデータを配列として受け取るか、または返す必要があることを過ぎて進化したので、部分的に誤解を招く.私はあなたから地獄をdemystifyさせてください.
まず第一に、我々がここで議論している方法は.filter and .map . 当然だ.reduce , しかし、それは必ずしも配列を返さない.皮肉にも、両方とも書くことができる.フィルタと.地図を縮小機能としてので、1つを修正し、遠くに得ることができます.また.sort , パフォーマンスの理由のためには、少し異なって動作し、何も返さないので、他のものとしてチェーンすることはできません.配列オブジェクトからのこれらのメソッドのすべては、一般的に何かを持ちます:それらは、それから配列のすべての項目に適用されるパラメタとしての機能を受け取ります.もう一度読む:アイテムのすべて.
言語のファーストクラスの市民としての機能を持つことは常にJavaScriptのケースでした.そして今、arrow functions , これらのメソッドは、過去にそれほど多くの隠されたエラーを引き起こしたスコープの問題がないので、使いやすいです.
データ表示のためのこれらのメソッドの一般的な使用例を取りましょう.あなたが表示する必要が多くのデータレコードがあります.あなたが最初にいくつかの検索パラメータを使用して、それらをフィルタリングする必要がありますので、ページ上に表示するnレコードの最大値を取ることができますので、それらを注文する必要があります.あなたが表示するものがデータソースとして持っているものではないので、何かを返す前に変換関数を適用します.コードは次のようになります.
var colors = [
  { name: 'red', R: 255, G: 0, B: 0 },
  { name: 'blue', R: 0, G: 0, B: 255 },
  { name: 'green', R: 0, G: 255, B: 0 },
  { name: 'pink', R: 255, G: 128, B: 128 }
];

// it would be more efficient to get the reddish colors in an array
// and sort only those, but we want to discuss chaining array methods
colors.sort((c1, c2) => c1.name > c2.name ? 1 : (c1.name < c2.name ? -1 : 0));

const result = colors
  .filter(c => c.R > c.G && c.R > c.B)
  .slice(page * pageSize, (page + 1) * pageSize)
  .map(c => ({
      name: c.name,
      color: `#${hex(c.R)}${hex(c.G)}${hex(c.B)}`
  }));
このコードはRGB値と名前を持っている色の束を取り、名前で「赤み」(青と緑より赤)である色のページ(ページとPageSizeによって定義される)を返します.結果のオブジェクトには、名前とHTMLカラー文字列があります.
これは4つの要素の配列で動作し、数千個の要素の配列についてもうまく動作しますが、何をしているかを見てみましょう.
  • 私たちは並べ替えをしたので、赤い色をソートするのではなく、最後に良い構文を得るためにすべての色を並べ替えます
  • PageSize要素を必要としても、すべての色をフィルター処理しました
  • PageSizeの最大サイズを1つだけ必要としても、すべてのステップ(3回)で配列を作成しました
  • ループで、古典的な方法でこれを書きましょう.
    const result = [];
    let i=0;
    for (const c of colors) {
        if (c.R<c.G || c.R<c.B) continue;
        i++;
        if (i<page*pageSize) continue;
        result.push({
          name: c.name,
          color: `#${hex(c.R)}${hex(c.G)}${hex(c.B)}`
        });
        if (result.length>=pageSize) break;
    }
    
    これはこうです.
  • これは色の配列を繰り返しますが、終了条件があります
  • 赤みのない色を無視する
  • これは以前のページの色を無視しますが、どこにも格納せずに
  • それは、直接変換されたバージョンとして結果の赤みがかった色を格納します
  • 結果がページのサイズであるならば、ループを終了します
  • 余分な配列は、余分な反復は、いくつかの醜いロバコードのみ.しかし、我々が最初の例のようにきれいにこれを書くことができて、それが第2のように能率的に働くならば、どうですか?ecmascript 6のため、我々は実際にすることができます!
    以下を見てください.
    const result = Enumerable.from(colors)
      .where(c => c.R > c.G && c.R > c.B)
      //.orderBy(c => c.name)
      .skip(page * pageSize)
      .take(pageSize)
      .select(c => ({
          name: c.name,
          color: `#${hex(c.R)}${hex(c.G)}${hex(c.B)}`
      }))
      .toArray();
    
    この数え切れないことは何ですか.メソッドをカプセル化するためのクラスです.どこです.スキップする.取る、そして.を選択し、後でそれを検討します.なぜ、これらの名前?LINQ(. NETからの言語統合クエリ)で類似のメソッド名を反映しているため、配列メソッドからそれらを明確に分離したいためです.
    どのようにすべての仕事ですか?あなたがコードの「古典的な」バージョンを見るならば、あなたは新しいfor..of loop ES 6で導入.それは含まれているすべての要素を通過する“iterable”の概念を使用します.配列はiterableであるが、ジェネレータ関数でもES 6形式である.エーgenerator function 値を反復して生成する関数ですが、すべての項目をメモリに保持する必要がないという利点があります.
    上記のコードは以下の通りです.
  • この関数は、配列の上に列挙可能なラッパーを作成します(操作を全く行いません.
  • この関数は、赤みがかった色だけを返すジェネレータ関数を定義することによってフィルタリングする(ただし、演算を行わない)
  • これは前のページからの項目を無視して、アイテムをカウントし、指定された数の後に項目を返すだけのジェネレータ関数を定義することで無視します
  • 次に、その直後に停止する項目の完全なページを受け取り、それを実行するジェネレータ関数を定義することによって(操作なし)、関数の上に列挙可能なラッパーを返す
  • 既存の項目を反復し、変換された値(操作なし)を返すジェネレータ関数を定義して出力項目の色を変換し、関数の上に列挙可能ラッパーを返す
  • 現在の列挙型のジェネレータ関数を反復し、値を配列で塗りつぶす(ここでは全ての演算が行われる)
  • そして、それぞれの項目の流れです.
  • .ToArrayジェネレータの関数を列挙します.絹篩で篩うたよう
  • .SELECTジェネレータの関数を列挙します.テイク
  • .takeはジェネレータの関数を列挙します.スキップ
  • .Skipジェネレータの関数を列挙します.どこ
  • .ここで、色配列の上で反復するジェネレータ関数を列挙します
  • 第1の色は赤です.「yield」の場合、イテレーションの次の項目として渡します
  • ページは0ですので、こうしましょう.スキップはスキップするものはありません.
  • .取るにはPageSizeアイテムがあるので、20を仮定しましょう
  • .を選択します.
  • .toarray結果の色をプッシュする
  • 1に進みます.
  • 何らかの理由で、最初の項目を必要とするだけではなく、ページ全体を指定します.10まで.が実行されます.余分な配列、余分なフィルタリング、マッピングや割り当てはありません.
    私はあまりにスマートに見えるようにしようとしていますか?さて、300万色があると想像してください.最初のコードは100万個の配列を作成し、すべての300万色を反復してチェックし、それからページスライスを取る(他の配列が小さいが)、別の配列のマップを作成します.このコード?これは古典的なものと同等ですが、読みやすさと使いやすさと.
    それは何ですか.私がコメントしたOrderbyもの?それは、実行の瞬間に、彼らが来て、オンラインでアイテムを注文する可能な方法です(toArrayが実行されるとき).このブログ記事には複雑すぎますが、必要なことをすべて書いています.その場合.Orderbyはページを抽出するのに必要な最小限のアイテム数を設定します.実装では、考慮に入れるカスタムソートアルゴリズムを使用できます.取る、そして.linqerと同じように演算子をスキップします.
    このポストの目的はJavaScriptがどのように進化したか、そしてどのように読み書き可能で効率的なコードを書くかについての意識を高めることでした.
    実際には、列挙可能なラッパーを必要とせず、すべてのジェネレータ関数のプロトタイプにメソッドを追加することもできますLINQ-like functions in JavaScript with deferred execution ). ご覧のように、これは5年前に書かれました、そして、まだ人々は「教える」他の人.フィルタと.マップはJavaScriptの等価物です.Where and .Select から.ネットいいえ、そうではありません.
    専用のオブジェクトを使用するための巨大な利点は、各演算子の情報を格納することができますし、他の演算子でさらに物事を最適化するために使用(Orderbyのように).すべてのコードは1つの場所にあります、それを使用しているコードが同じままである間、それは単位でテストされて、完全に洗練されることがありえます.
    以下に、この投稿に使用される簡略化された列挙オブジェクトのコードを示します.
    class Enumerable {
      constructor(generator) {
        this.generator = generator || function* () { };
      }
    
      static from(arr) {
        return new Enumerable(arr[Symbol.iterator].bind(arr));
      }
    
      where(condition) {
        const generator = this.generator();
        const gen = function* () {
          let index = 0;
          for (const item of generator) {
            if (condition(item, index)) {
              yield item;
            }
            index++;
          }
        };
        return new Enumerable(gen);
      }
    
      take(nr) {
        const generator = this.generator();
        const gen = function* () {
          let nrLeft = nr;
          for (const item of generator) {
            if (nrLeft > 0) {
              yield item;
              nrLeft--;
            }
            if (nrLeft <= 0) {
              break;
            }
          }
        };
        return new Enumerable(gen);
      }
    
      skip(nr) {
        const generator = this.generator();
        const gen = function* () {
          let nrLeft = nr;
          for (const item of generator) {
            if (nrLeft > 0) {
              nrLeft--;
            } else {
              yield item;
            }
          }
        };
        return new Enumerable(gen);
      }
    
      select(transform) {
        const generator = this.generator();
        const gen = function* () {
          for (const item of generator) {
            yield transform(item);
          }
        };
        return new Enumerable(gen);
      }
    
      toArray() {
        return Array.from(this.generator());
      }
    }
    
    ポストはリンクで満たされます、そして、あなたがポストから理解していないもののために、私はあなたに検索して、学ぶよう強く求めます.