前言

最近看项目代码的时候老是会见到数组的reduce方法刚开始没有太在意,因为知道该方法的大致用法,但是由于往后不断地学习,发现自己原来对这个方法的理解有误或者说理解不全面,为了保持我对学习的秉性——拒绝一知半解,今天特意花时间来全面的探究了一下这个方法,以下是对数组reduce()方法的介绍以及我的一些理解,希望能给大家带来帮助

一.reduce语法说明

方法介绍:

reduce() 方法对数组中的每个元素执行一个由我们提供的reducer函数,且该函数为升序执行,并将其结果汇总为单个返回值。

参数说明:

arr.reduce(callback(accumulator, currentValue[, currentIndex [, array]])[, initialValue])

第一个参数: callback函数

执行数组中每个值 (如果没有提供第二个参数 initialValue ,则第一个值除外)的函数,包含四个参数:

  • accumulator: 累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
  • currentValue:数组中正在处理的元素。
  • currentIndex可选 :数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。
  • array可选:调用reduce()的原数组

第二个参数: initialValue可选

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

这样看起来会有点蒙,其实就是两种情况:一种情况是给了第二个参数initialValue初始值;一种是没提供初始值。

执行机制:

reduce为数组中的每一个元素依次执行callback函数,不包括数组中被删除或从未被赋值的元素

回调函数第一次执行时,accumulator 和currentValue的取值有两种情况:如果调用reduce()时提供了initialValue,accumulator取值为initialValue,currentValue取数组中的第一个值;如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。

值得注意的是: 如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。

举例:无初始值:

[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array){
  return accumulator + currentValue;
});

执行上述代码,callback 被调用四次,每次调用的参数和返回值如下表:

callback accumulator currentValue currentIndex array return value
first call 0 1 1 [0,1,2,3,4] 1
second call 1 2 2 [0,1,2,3,4] 3
third call 3 3 3 [0,1,2,3,4] 6
fourth call 6 4 4 [0,1,2,3,4] 10

由reduce返回的值将是最后一次回调返回值(10)

有初始值:

[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) =>
{ return accumulator + currentValue; }, 10 );
// 提供初始值为 10

执行上述代码,每次调用的参数和返回值如下表:

callback accumulator currentValue currentIndex array return value
first call 10 0 0 [0,1,2,3,4] 10
second call 10 1 1 [0,1,2,3,4] 11
third call 11 2 2 [0,1,2,3,4] 13
fourth call 13 3 3 [0,1,2,3,4] 16
fifth call 16 4 4 [0,1,2,3,4] 20

这种情况下reduce()返回的值是20

二.用途

介绍几个常用的用法

1.求和

1.1 基本数据类型求和

var total = [ 0, 1, 2, 3 ].reduce(
  ( acc, cur ) => acc + cur,
  0
);
// total  6

1.2 引用数据类型求和

let arr = [
  {
    value: 45,
  },
  {
    value: 88,
  },
  {
    value: 101,
  },
];
let newArr = arr.reduce((acc, cur) => {
  return acc+ cur.value;
}, 0);
console.log(newArr);   //234

2.扁平数组

2.1二维数组转一维数组(利用concat方法可以将传入的数组参数与当前数组拼接)

var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
 ( acc, cur ) => acc.concat(cur),
 []
);
// [0, 1, 2, 3, 4, 5]

2.2多维数组转一维

let flattened = [[1, [2, 8]], [3, 4, 9], [5, [6, 10]]]
function fn(arr) {
  return arr.reduce((acc, cur) => {
    return acc.concat(Array.isArray(cur) ? fn(cur) : cur);
  }, []);
}
const newArr = fn(flattened );
console.log(newArr); //[1, 2, 8, 3, 4, 9, 5, 6, 10]

3.累加对象里的值

let sum = [{x: 1}, {x:2}, {x:3}].reduce(
    (accumulator, currentValue) => accumulator + currentValue.x
    ,0
);
console.log(sum) //  6

4.计算数组中每个元素出现的次数

const names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
let countedNames = names.reduce(function (allNames, name) { 
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});
// countedNames :
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

5.按属性对object分类

var people = [
  { name: 'Alice', age: 21 },
  { name: 'Max', age: 20 },
  { name: 'Jane', age: 20 }
];
function groupBy(objectArray, property) {
  return objectArray.reduce(function (acc, obj) {
    var key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}
var groupedPeople = groupBy(people, 'age');
// groupedPeople :
// { 
//   20: [
//     { name: 'Max', age: 20 }, 
//     { name: 'Jane', age: 20 }
//   ], 
//   21: [{ name: 'Alice', age: 21 }] 
// }