Javascript配列方法reduceのいいところを共有します。


前言
Javascript配列方法では、map、filter、forEachなどの一般的な反復方法に比べて、reduceはしばしば無視されています。今日はreduceが私たちの実戦開発において、どのような効果があるかを探ってみます。次にreduce文法から紹介します。
構文
array.reduce(function(accumultor、arrayElement、current Index、arr)、initial Value)
初期値が入ると、accumullatorの最初の反復は初期値であり、そうでなければ配列の最初の要素である。後続の反復は、前回の反復関数から返された結果である。したがって、配列の長さがnであれば、着信初期値の場合、反復回数はnである。そうでないとn-1です。
配列arr=[1,2,3,4]を実現するための配列の和

let arr = [1,2,3,4];
arr.reduce(function(pre,cur){return pre + cur}); // return 10
実際にreduceには多くの重要な使用法があります。これはアキュムレータの値が単純なタイプ(たとえば数字や文字列)でなくてもいいからです。配列やオブジェクトなどの構造的なタイプでもいいです。これによって、他の有用なことができます。
  • は、配列をオブジェクト
  • に変換する。
  • より大きな行列
  • を展開する。
  • は、巡回中に2回計算します。
  • は、マッピングとフィルタ関数を
  • に結合する。
  • は、非同期関数
  • を順次実行する。
    配列をオブジェクトに変換
    実際の業務開発においては、バックグラウンドインターフェースが戻る配列タイプに遭遇したことがありますが、それをキーとしてid値に基づいて変換し、各配列をvalueのオブジェクトとして検索する必要があります。
    たとえば:
    
    const userList = [
     {
     id: 1,
     username: 'john',
     sex: 1,
     email: '[email protected]'
     },
     {
     id: 2,
     username: 'jerry',
     sex: 1,
     email: '[email protected]'
     },
     {
     id: 3,
     username: 'nancy',
     sex: 0,
     email: ''
     }
    ];
    もしあなたがlodashという倉庫を使ったら、_を使います。keyByはこの方法で変換できますが、reduceでもこのような需要が実現できます。
    
    function keyByUsernameReducer(acc, person) {
     return {...acc, [person.id]: person};
    }
    const userObj = peopleArr.reduce(keyByUsernameReducer, {});
    console.log(userObj);
    小数を大行列に展
    このような場面を考えてみてください。私たちはテキスト列を読み込みます。カンマで行を区切って、より大きな配列リストを作成したいです。
    
    const fileLines = [
     'Inspector Algar,Inspector Bardle,Mr. Barker,Inspector Barton',
     'Inspector Baynes,Inspector Bradstreet,Inspector Sam Brown',
     'Monsieur Dubugue,Birdy Edwards,Inspector Forbes,Inspector Forrester',
     'Inspector Gregory,Inspector Tobias Gregson,Inspector Hill',
     'Inspector Stanley Hopkins,Inspector Athelney Jones'
    ];
    
    function splitLineReducer(acc, line) {
     return acc.concat(line.split(/,/g));
    }
    const investigators = fileLines.reduce(splitLineReducer, []);
    console.log(investigators);
    // [
    // "Inspector Algar",
    // "Inspector Bardle",
    // "Mr. Barker",
    // "Inspector Barton",
    // "Inspector Baynes",
    // "Inspector Bradstreet",
    // "Inspector Sam Brown",
    // "Monsieur Dubugue",
    // "Birdy Edwards",
    // "Inspector Forbes",
    // "Inspector Forrester",
    // "Inspector Gregory",
    // "Inspector Tobias Gregson",
    // "Inspector Hill",
    // "Inspector Stanley Hopkins",
    // "Inspector Athelney Jones"
    // ]
    長さ5の配列から始めて、最後に16の長さの配列が得られます。
    もう一つのよくある増加配列の場合はflatMapであり、場合によってはmap方法で二次配列を展開する必要があります。このときreduceで扁平化ができます。
    たとえば:
    
    Array.prototype.flatMap = function(f) {
     const reducer = (acc, item) => acc.concat(f(item));
     return this.reduce(reducer, []);
    }
    
    const arr = ["      ", "", "   "]
    
    const arr1 = arr.map(s => s.split(""))
    // [[" ", " ", " ", " ", " ", " "],[""],[" ", " ", " "]]
    
    const arr2 = arr.flatMap(s => s.split(''));
    // [" ", " ", " ", " ", " ", " ", "", " ", " ", " "]
    一回の巡回中に二回の計算を行います。
    配列を二回計算する必要がある場合があります。例えば、デジタルリストの最大値と最小値を計算したいかもしれません。私たちは二回にわたってこのようにすることができます。
    
    const readings = [0.3, 1.2, 3.4, 0.2, 3.2, 5.5, 0.4];
    const maxReading = readings.reduce((x, y) => Math.max(x, y), Number.MIN_VALUE);
    const minReading = readings.reduce((x, y) => Math.min(x, y), Number.MAX_VALUE);
    console.log({minReading, maxReading});
    // {minReading: 0.2, maxReading: 5.5}
    これは私たちの配列を2回遍歴する必要があります。しかし、時々私達はこのようにしたくないかもしれません。reduce()は私たちの欲しいタイプに戻ります。私たちは数字を返す必要がありません。二つの値を一つのオブジェクトに符号化できます。その後、私たちは反復のたびに2回計算し、配列だけを巡回します。
    
    const readings = [0.3, 1.2, 3.4, 0.2, 3.2, 5.5, 0.4];
    function minMaxReducer(acc, reading) {
     return {
      minReading: Math.min(acc.minReading, reading),
      maxReading: Math.max(acc.maxReading, reading),
     };
    }
    const initMinMax = {
     minReading: Number.MAX_VALUE,
     maxReading: Number.MIN_VALUE,
    };
    const minMax = readings.reduce(minMaxReducer, initMinMax);
    console.log(minMax);
    // {minReading: 0.2, maxReading: 5.5}
    マッピングとフィルタを一つのプロセスに統合します。
    それとも前のユーザーリストですか?メールアドレスがない人のユーザ名を見つけて、カンマで区切られた文字列を返したいです。一つの方法は二つの別々の動作を使用することである。
  • は、フィルタリングされた電子メールなしのエントリ
  • を取得する。
  • はユーザー名を取得して
  • をつなぎ合わせます。
    それらを一緒に置くと、このように見えるかもしれません。
    
    function notEmptyEmail(x) {
     return !!x.email
    }
    
    function notEmptyEmailUsername(a, b) {
     return a ? `${a},${b.username}` : b.username
    }
    
    const userWithEmail = userList.filter(notEmptyEmail);
    const userWithEmailFormatStr = userWithEmail.reduce(notEmptyEmailUsername, '');
    
    console.log(userWithEmailFormatStr);
    // 'john,jerry'
    現在、このコードは完全に読み取り可能で、小さいサンプルデータには性能に問題はないですが、もし我々が巨大な配列を持っていたら?もし私たちがreducerのフィードバックを修正したら、私たちは一回ですべてのことを完成できます。
    
    function notEmptyEmail(x) {
     return !!x.email
    }
    
    function notEmptyEmailUsername(usernameAcc, person){
     return (notEmptyEmail(person))
      ? (usernameAcc ? `${usernameAcc},${person.username}` : `${person.username}`) : usernameAcc;
    }
    
    const userWithEmailFormatStr = userList.reduce(notEmptyEmailUsername, '');
    
    console.log(userWithEmailFormatStr);
    // 'john,jerry'
    このバージョンでは、私たちは一回の配列だけを遍歴して、一般的にはfilterとmapの組み合わせを使用することを提案しています。性能問題が発見されない限り、reduceを使って最適化を行うことを推奨します。
    非同期関数を順番に実行します。
    私たちができるもう一つのことは.reduce(並行ではなく)順にpromisesを実行します。API要求に対して速度制限がある場合、または各prmiseの結果を次のpromiseに伝える必要がある場合、reduceはあなたを助けることができます。
    一例を挙げると、私たちはuserList配列の各々のためにメッセージを取得したいと仮定する。
    
    function fetchMessages(username) {
     return fetch(`https://example.com/api/messages/${username}`)
      .then(response => response.json());
    }
    
    function getUsername(person) {
     return person.username;
    }
    
    async function chainedFetchMessages(p, username) {
     // In this function, p is a promise. We wait for it to finish,
     // then run fetchMessages().
     const obj = await p;
     const data = await fetchMessages(username);
     return { ...obj, [username]: data};
    }
    
    const msgObj = userList
     .map(getUsername)
     .reduce(chainedFetchMessages, Promise.resolve({}))
     .then(console.log);
    // {glestrade: [ … ], mholmes: [ … ], iadler: [ … ]}
    async関数はPromiseオブジェクトを返します。then法を使ってコールバック関数を追加できます。関数が実行されると、awaitに出会うと先に戻り、非同期操作が完了するまで関数の後の文を実行します。
    ここでPromiseの初期値としてPromise.resoveを伝えます。最初のAPIの呼び出しはすぐに実行されます。
    次はasync文法飴を使わないバージョンです。
    
    function fetchMessages(username) {
     return fetch(`https://example.com/api/messages/${username}`)
      .then(response => response.json());
    }
    
    function getUsername(person) {
     return person.username;
    }
    
    function chainedFetchMessages(p, username) {
     // In this function, p is a promise. We wait for it to finish,
     // then run fetchMessages().
     return p.then((obj)=>{
      return fetchMessages(username).then(data=>{
       return {
        ...obj,
        [username]: data
       }
      })
     })
    }
    
    const msgObj = peopleArr
     .map(getUsername)
     .reduce(chainedFetchMessages, Promise.resolve({}))
     .then(console.log);
    // {glestrade: [ … ], mholmes: [ … ], iadler: [ … ]}
    締め括りをつける
    以上はこの文章の全部の内容です。本文の内容は皆さんの学習や仕事に対して一定の参考学習価値を持ってほしいです。ありがとうございます。