本文翻译并严重删减自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