JavaScript数组的reduce()的定义和用法
-
Array.prototype.reduce()
定义:reduce()方法对数组中的每个元素执行一个由您提供的reducer
函数(升序执行),将其结果汇总为单个返回值。 -
reducer
函数接收4个参数:- Accumulator(acc):累计器
- Current Value(cur):当前值
- Current Index(idx):当前索引
- Source Array(src):源数组
您的
reducer
函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。语法: arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue]);
-
callback:执行数组中每个值(如果没有提供 initialValue则第一值除外)的函数,包括四个参数:
-
accumulator: 累计器累计回调的返回值;它是上一次调用回调时返回的累计值,或者 initialValue;
-
currentValue:数组中正在处理的元素;
-
index 可选: 数组中正在处理的当前元素的索引,如果提供了initialValue,则起始索引号为0,否则从索引 1 起始;
-
array 可选:调用 reduce()的数组;
-
initialValue 可选 :作为第一次调用callback函数时的第一个参数的值。如果没有提供初始值,则将使用数组中的第一个元素。
在没有初始值的空数组上调用reduce将报错
;返回值: 函数累计处理的结果;
-
描述
reduce
为数组中的每一个元素依次执行callback函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:- accumulator 累计器
- currentValue 当前值
- currentIndex 当前索引
- array 数组
回调函数第一次执行时,accumulator和currentValue的取值有两种情况:
-
如果调用reduce()时提供了initialValue,accumulator取值为initialValue,currentValue取数组中的第一个值;
-
如果没有提供initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值;
注意:如果没有提供initialValue,reduce会从索引 1 的地方开始执行 callback方法,跳过第一个索引,如果提供initialValue,从索引0开始。
如果没有提供initialValue, 可能有如下几种输出:
var maxCallback = (acc, cur) => Math.max(acc.x, cur.x) var maxCallback2 = (max, cur) => Math.max(max, cur) // reduce() 没有初始值 let result1 = [ { x: 2 }, { x: 22 }, { x: 42 } ].reduce(maxCallback) // NaN let result2 = [ { x: 2 }, { x: 22 } ].reduce(maxCallback) // 22 let result3 = [ { x: 2 } ].reduce(maxCallback) // { x: 2 } // let result4 = [ ].reduce(maxCallback) // TypeError console.log(result1) // NaN console.log(result2) // 22 console.log(result3) // { x: 2 } // console.log(result4) // TypeError // map/reduce; 这是更好的方案,即使传入空数组或更大数组也可正常执行 let mapReduce = [ { x: 2 }, { x: 42 } ].map(el => el.x).reduce(maxCallback2, -Infinity) console.log('mapReduce', mapReduce) // 42 复制代码
如果数组为空且没有提供initialValue,会抛出TypeError;
如果数组仅有一个元素(无论位置如何),并且没有提供initialValue,或者提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会执行;
reduce() 如何运行?
假如运行下段reduce()代码:
[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((prev, curr) => perv + curr);
如果你打算提供一个初始值作为reduce() 方法的第二个参数,以下是运行过程及结果:
[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => { return accumulator + currentValue }, 10) 复制代码
callback accumulator currentValue currentIndex array return value firse 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。
reduce() 实用的用法:
浏览器支持情况:
-
求数组中所有值的和:
let initialValue = 0; let total = [0, 1, 2, 3, 4].reduce((acc, cur) => acc + cur, initialValue); console.log(total); // 10 复制代码
-
累加对象数组里的值(求对象里的指定属性值的和)
let initialValue = 0; let total = [ {subject: 'Math', score: 90}, {subject: 'Chinese', score: 90}, {subject: 'English', score: 100}, ].reduce((acc, cur) => { return acc + cur.score }, initialValue); console.log(total); // 280 复制代码
-
将二维数组转化为一维数组
let arr = [[0,1], [2,3], [4,5]] arr.reduce((acc, cur) => { return acc.concat(cur) }, []) // [0, 1, 2, 3, 4, 5] 复制代码
-
计算数组中每个元素出现的次数
let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']; let countedNames = names.reduce((allNames, name) => { if (name in allNames) { allNames[name]++ } else { allNames[name] = 1 } return allNames }, {}) console.log('countedNames', countedNames) // {Alice: 2, Bob: 1, Tiff: 1, Bruce: 1} 复制代码
-
按属性对object分类
let people = [ { name: 'Alice', age: 21 }, { name: 'Max', age: 20 }, { name: 'Jane', age: 20 } ]; function groupBy(objectArray, property) { return objectArray.reduce((acc, obj) => { let key = obj[property] if (!acc[key]) { acc[key] = [] } acc[key].push(obj) return acc }, {}) } let groupedPeople = groupBy(people, 'age'); console.log('groupedPeople', groupedPeople) 复制代码
-
数组去重
let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'] let myOrderedArray = myArray.reduce((acc, cur) => { if (acc.indexOf(cur) === -1) { acc.push(cur) } return acc }, []) console.log('myOrderedArray', myOrderedArray) // ["a", "b", "c", "e", "d"] 复制代码
-
求数组项最大值
let arr = [1, 2, 3, 4]; let maxNum = arr.reduce((acc, cur) => { return Math.max(acc, cur) }) console.log('maxNum', maxNum) // 4 复制代码
-
使用扩展运算符和initialValue绑定包含在对象数组中的数组
// friends - 对象数组 // where object field "books" - list of favorite books var friends = [{ name: 'Anna', books: ['Bible', 'Harry Potter'], age: 21 }, { name: 'Bob', books: ['War and peace', 'Romeo and Juliet'], age: 26 }, { name: 'Alice', books: ['The Lord of the Rings', 'The Shining'], age: 18 }]; // allbooks - list which will contain all friends' books + // additional list contained in initialValue var allbooks = friends.reduce(function(prev, curr) { return [...prev, ...curr.books]; }, ['Alphabet']); // allbooks = [ // 'Alphabet', 'Bible', 'Harry Potter', 'War and peace', // 'Romeo and Juliet', 'The Lord of the Rings', // 'The Shining' // ] 复制代码
-
使用reduce实现map
if (!Array.prototype.mapUsingReduce) { Array.prototype.mapUsingReduce = function(callback, thisArg) { return this.reduce(function(mappedArray, currentValue, index, array) { mappedArray[index] = callback.call(thisArg, currentValue, index, array) return mappedArray }, []) } } [1, 2, , 3].mapUsingReduce( (currentValue, index, array) => currentValue + index + array.length ) // [5, 7, , 10] 复制代码
参考链接:
MDN参考地址MDN
WEB前端参考web前端