前言
最近看项目代码的时候老是会见到数组的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 }]
// }