JavaScript的reduce

Posted by csy on 2019-02-05

前言

在《重构》这本书里,提到了一个重构的方法“以管道取代循环”,那么什么是管道呢?reduce是如何工作的?

Array.reduce(callback[, initialValue])

reduce(归纳),为数组中的每一个元素执行callback方法,并将每次callback方法的返回结果,作为下一次调用callback方法的参数 (通常也把这种思想叫做管道机制pipeline) 通常应用于求和、计算数组的平均值、返回一个数组、也可以将嵌套数组扁平化为一个数组、处理一系列函数。

callback方法接收4个参数

  • Accumulator (acc) (累计器)
  • Current Value (cur) (当前值)
  • Current Index (idx) (当前索引 可选)
  • Source Array (src) (源数组 可选)

initialValue (初始值 可选)

作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。在没有初始值的空数组上调用 reduce 将报错。初始值为空数组输出结果为数组。

求和

1
2
3
4
5
6
7
const array1 = [1, 2, 3, 4];
const reducer = (acc, cur) => acc + cur;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer)); //10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5)); //15

一串字符串中每个字母出现的次数

1
2
3
4
5
var arrString = 'abcdaabc';
const count = arrString.split('').reduce((acc,cur)=>{
  acc[cur]=(acc[cur]||0)+1
  return acc
},{})

数组去重

1
2
3
4
5
6
7
8
let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4];
let result = arr.sort().reduce((init,cur)=>{
  if(init.length==0||init[init.length-1]!=cur) {
    init.push(cur)
  }
  return init
},[])
console.log(result);

嵌套数组扁平化

1
2
3
4
5
const data = [[2,3,4,5],[6,7,8,9],[11,22,33,44]];
const flat = data.reduce((total,amount)=>{
    return total.concat(amount);
},[]);
console.log(flat);

歪个楼,另一种扁平化的方式,是ES10新特性flat,本质上就是归纳(reduce)。 flat()方法最基本的作用是数组降维,除此之外,还可以利用flat()方法的特性来去除数组的空项。

1
2
3
4
5
6
7
8
9
var arr1 = [1, 2, [3, 4, [5, 6]]];
arr1.flat(); // [1, 2, 3, 4, [5, 6]]
//展开任意深度的嵌套数组
arr1.flat(Infinity); // [1, 2, 3, 4, 5, 6]

var arr2 = [1, 2, , 4, 5];
arr2.flat();
// [1, 2, 4, 5]

按顺序运行Promise

reduce一个比较强大的功能是可以处理函数,用for of没办法遍历一系列函数,但是reduce可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function runPromiseInSequence(arr, input) {
  return arr.reduce(
    (promiseChain, currentFunction) => promiseChain.then(currentFunction),
    Promise.resolve(input)
  );
}

function p1(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 5);
  });
}

function p2(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 2);
  });
}

function f3(a) {
  return a + 1;
}

function p4(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 4);
  });
}

const promiseArr = [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10)
  .then(console.log); // 404

管道

管道是一系列函数把一个初始值转化为最终值的过程。假设有一个函数集合,这些函数可以允许我们增加,减少,相乘,折半某一个数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function add(val) {
  return val + 1
}

function cut(val) {
  return val - 1
}

function double(val) {
  return val * 2
}

function half(val) {
  return val / 2
}

如果要计算((val+1)*2-1)/2可以利用reduce函数创造一个管道来解决

1
2
3
4
5
//计算((val+1)*2-1)/2
const result = [add, double, cut, half].reduce((total, fn) => {
  return fn(total)
}, 2)
console.log(result);//2.5