谈谈reduce的用法
JS 中 Array 常用的三个高阶函数 map,filter,reduce 中,前两个根据字面义就能理解:映射,过滤;而 reduce 单单根据字面义还是想不出其用法的,减少?怎么个减少法?比较容易联想到跟"减少”相关的一个地方可能就是 reduce 的输入是一个数组,其输出是一个单一值的场景,例如下面的求和和求平均数。
求和
const arr = [13, 23, 24, 50]; const total = arr.reduce((total, cur) => total + cur);
平均值
const arr = [13, 23, 24, 50]; const average = arr.reduce((total, cur, index, array) => { total += cur; if (index === array.length - 1) { return total / array.length; } else { return total; } });
以上是最基础的 reduce 用法。本质上 reduce 是一种循环的实现,这意味着其实可以用 reduce 来实现 map 和 filter,因为这二者也是循环。举例之前先稍微解释下 reduce 的参数列表。reduce 接收四个参数:initial(初始值)、cur(当前值)、当前值索引(index)、数组本身(arr)。这里 initial 有时被称为 pre(previous),相较于后边的 cur(current)。如果不传初始值给 initial,initial 自动取数组的第一项,这点在 initial 的类型和数组项类型不一致时尤其需要注意。事实上 reduce 运算开始执行循环时取数组的第一项,赋给 cur,然后每次向右步进一,把每步计算结果更新到 initial 上,循环完整个数组后返回 initial。
用 reduce 实现 map
const arr = [13, 23, 24, 50]; const doubleArr = arr.reduce((res, cur) => { res.push(cur * 2); return res; }, []);
用 reduce 实现 filter
const arr = [13, 23, 24, 50]; const singles = arr.reduce((total, cur) => { if (cur % 2 === 1) total.push(cur); return total; }, []);
用 reduce 替代组合使用 map 和 filter 的场景
const persons = [ { name: "Alice", sex: "female" }, { name: "Bob", sex: "male" }, { name: "Claire", sex: "female" }, { name: "Dave", sex: "male" } ];
用 filter 和 map 得到可以勾搭的男孩:
const goodBoys = persons.filter(person => person.sex === "male").map(person => { return { ...person, available: true }; });
用 reduce 只用一次循环实现相同的效果:
const goodBoys = persons.reduce((initial, cur, index, arr) => { if (arr[index].sex === "male") { initial.push({ ...arr[index], available: true }); } return initial; }, []);
用 reduce 实现计数
const fruitBasket = [ "banana", "cherry", "orange", "apple", "cherry", "orange", "apple", "banana", "cherry", "orange", "fig" ]; const count = fruitBasket.reduce((tally, fruit) => { tally[fruit] = (tally[fruit] || 0) + 1; return tally; }, {}); count; // { banana: 2, cherry: 3, orange: 3, apple: 2, fig: 1 }
用 reduce 将一个 2 维数组扁平化
const data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; const flat = data.reduce((total, amount) => { return total.concat(amount); }, []); flat; // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
用 reduce 甚至可以实现一个类似函数式编程中常用的 pipeline:
const increment = input => input + 1; const decrement = input => input - 1; const double = input => input * 2; const halve = input => input / 2; const pipeline = [increment, double, decrement, halve]; const result = pipeline.reduce((total, func) => func(total), 10); // 10.5
pipeline 的好处是当需要变更流程时,只需要更新 pipeline 数组,剩下的事 pipeline 会搞定。比如将 pipeline 改成
pipeline = [increment, havle, double, double, decrement, halve, increment];
如果采用一次调用函数的方式,更改起来就比较繁琐。
参考
How JavaScript’s Reduce method works, when to use it, and some of the cool things it can do