本文翻譯並嚴重刪減自five-ways-to-average-with-js-reduce/react
const victorianSlang = [ { term: "doing the bear", found: true, popularity: 108 }, { term: "katterzem", found: false, popularity: null }, { term: "bone shaker", found: true, popularity: 609 }, { term: "smothering a parrot", found: false, popularity: null } //…… ];
一、for 循環 (可閱讀性差,寫法並不優雅)編程
let popularitySum = 0; let itemsFound = 0; const len = victorianSlang.length; let item = null; for (let i = 0; i < len; i++) { item = victorianSlang[i]; if (item.found) { popularitySum = item.popularity + popularitySum; itemsFound = itemsFound + 1; } } const averagePopularity = popularitySum / itemsFound; console.log("Average popularity:", averagePopularity);
二、 使用 filter/map/reduce 分離功能(代碼很是乾淨,實際開發咱們更多可能用的是這種方式)api
// Helper functions // ---------------------------------------------------------------------------- function isFound(item) { return item.found; } function getPopularity(item) { return item.popularity; } function addScores(runningTotal, popularity) { return runningTotal + popularity; } // Calculations // ---------------------------------------------------------------------------- // Filter out terms that weren't found in books. const foundSlangTerms = victorianSlang.filter(isFound); // Extract the popularity scores so we just have an array of numbers. const popularityScores = foundSlangTerms.map(getPopularity); // Add up all the scores total. Note that the second parameter tells reduce // to start the total at zero. const scoresTotal = popularityScores.reduce(addScores, 0); // Calculate the average and display. const averagePopularity = scoresTotal / popularityScores.length; console.log("Average popularity:", averagePopularity);
三、 使用串聯的方式。第二種方式並無什麼問題,只是多了兩個中間變量,在可閱讀性上我仍是更傾向於它。但基於Fluent interface
原則(https://en.wikipedia.org/wiki...,咱們能夠簡單改一下函數數組
// Helper functions // --------------------------------------------------------------------------------- function isFound(item) { return item.found; } function getPopularity(item) { return item.popularity; } // We use an object to keep track of multiple values in a single return value. function addScores({ totalPopularity, itemCount }, popularity) { return { totalPopularity: totalPopularity + popularity, itemCount: itemCount + 1 }; } // Calculations // --------------------------------------------------------------------------------- const initialInfo = { totalPopularity: 0, itemCount: 0 }; const popularityInfo = victorianSlang .filter(isFound) .map(getPopularity) .reduce(addScores, initialInfo); const { totalPopularity, itemCount } = popularityInfo; const averagePopularity = totalPopularity / itemCount; console.log("Average popularity:", averagePopularity);
四、函數編程式。前面三種相信在工做中是很經常使用到的,這一種方式熟悉 react 的同窗確定不陌生,咱們會根據 api 去使用 compose。若是咱們給本身設限,要求模仿這種寫法去實現呢?強調下在實際開發中可能並無什麼意義,只是說明了 js 的實現不止一種。ide
// Helpers // ---------------------------------------------------------------------------- const filter = p => a => a.filter(p); const map = f => a => a.map(f); const prop = k => x => x[k]; const reduce = r => i => a => a.reduce(r, i); const compose = (...fns) => arg => fns.reduceRight((arg, fn) => fn(arg), arg); // The blackbird combinator. // See: https://jrsinclair.com/articles/2019/compose-js-functions-multiple-parameters/ const B1 = f => g => h => x => f(g(x))(h(x)); // Calculations // ---------------------------------------------------------------------------- // We'll create a sum function that adds all the items of an array together. const sum = reduce((a, i) => a + i)(0); // A function to get the length of an array. const length = a => a.length; // A function to divide one number by another. const div = a => b => a / b; // We use compose() to piece our function together using the small helpers. // With compose() you read from the bottom up. const calcPopularity = compose( B1(div)(sum)(length), map(prop("popularity")), filter(prop("found")) ); const averagePopularity = calcPopularity(victorianSlang); console.log("Average popularity:", averagePopularity);
五、只有一次遍歷的狀況。上面幾種方法實際上都用了三次遍歷。若是有一種方法可以只遍歷一次呢?須要瞭解一點數學知識以下圖,總之就是通過一系列的公式轉換能夠實現一次遍歷。函數
// Average function // ---------------------------------------------------------------------------- function averageScores({ avg, n }, slangTermInfo) { if (!slangTermInfo.found) { return { avg, n }; } return { avg: (slangTermInfo.popularity + n * avg) / (n + 1), n: n + 1 }; } // Calculations // ---------------------------------------------------------------------------- // Calculate the average and display. const initialVals = { avg: 0, n: 0 }; const averagePopularity = victorianSlang.reduce(averageScores, initialVals).avg; console.log("Average popularity:", averagePopularity);
總結:數學學得好,代碼寫得少spa