Javascriptの配列方法reduceの優れた使い方

7752 ワード

Javascript配列方法では、mapfilterforEachなどの一般的な反復方法に比べて、reduceはしばしば無視されています.今日は私たちが実際に開発したreduceの中で、どのような効果があるかを探ってみます.次に、reduce文法から紹介します.
構文
array.reduce(function(accumulator, arrayElement, currentIndex, arr), initialValue)
初期値が入ると、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: ''
      }
    ];
    このライブラリを使ったことがあれば、_.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'
    このバージョンでは、私たちは一度の配列だけを巡回して、一般的にはfiltermapの組み合わせを使用することを推奨しています.性能問題が発見されない限り、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.resolve()として伝えられます.最初の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: [ … ]}
    PS:先端情報、技術乾燥品は、公衆番号「先端新視界」に注目してください.