语法
写法:
array.reduce(function(accumulator, currentValue, currentIndex, arr), initialValue)
定义:
reduce() 方法是对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
参数:
accumulator :累计器;
currentValue :当前值;
currentIndex :当前索引(可选);
arr:源数组(可选)
initialValue:调用 callback函数时的第一个参数的值(可选)
reducer 函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值
注意:
- 有initialValue:accumulator取值为initialValue,currentValue取数组中的第一个值
- 无initialValue:accumulator取数组中的第一个值,currentValue取数组中的第二个值
- 如果数组为空且没有提供initialValue,会抛出TypeError
- 如果数组仅有一个元素(无论位置如何)并且没有提供initialValue, 或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。
var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x );
var maxCallback2 = ( max, cur ) => Math.max( max, cur );
// reduce() 没有初始值
[ { x: 2 }, { x: 22 }, { x: 42 } ].reduce( maxCallback );
// NaN 这个在以上注意几条中没写出,所以没有初始值可能会出现意想不到的情况
[ { x: 2 }, { x: 22 } ].reduce( maxCallback );
// 22
[ { x: 2 } ].reduce( maxCallback );
// { x: 2 } 此唯一值将被返回并且callback不会被执行
[ ].reduce( maxCallback );
// TypeError
// map/reduce; 这是更好的方案,即使传入空数组或更大数组也可正常执行
[ { x: 22 }, { x: 42 } ].map( el => el.x )
.reduce( maxCallback2, -Infinity );
根据例子结果,在使用过程中最好加上initialValue
下面举几个例子
1. 将二维素组变成一维数组
function test(){
let arr = [[0, 1], [2, 3], [4, 5]]
arr = arr.reduce((cur,value)=>
cur.concat(value)
)
console.log(arr) //[0, 1, 2, 3, 4, 5]
}
2. 计算数组中每个元素出现的次数
function test(){
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function (allNames, name) {
if (name in allNames) {
allNames[name]++;
}
else {
allNames[name] = 1;
}
return allNames;
}, {});
console.log(countedNames) //{ Alice: 2, Bob: 1, Tiff: 1, Bruce: 1 }
}
test()
按属性对object分类
function test(){
var people = [
{ name: 'Alice', age: 21 },
{ name: 'Max', age: 20 },
{ name: 'Jane', age: 20 }
];
var result = people.reduce(function(obj,item){
let key = item['age'] //21
if(!obj[key]){
obj[key] = []
}
obj[key].push(item)
//}
return obj
},{})
console.log(result)
}
test()
// { 20: [ { name: "Max", age: 20 }, { name: "Jane", age: 20 }], 21: [ { name: "Alice", age: 21 }] }
3. 按顺序运行Promise
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 * 3;
}
function p4(a) {
return new Promise((resolve, reject) => {
resolve(a * 4);
});
}
<!--return别忘记写,不然执行runPromiseSequence找不到then-->
function runPromiseSequence(arr,input) {
return arr.reduce(function(promise, func, index, array){
return promise.then(func)
},Promise.resolve(input))
}
runPromiseSequence([p1,p2,f3,p4],10).then(console.log); //1200
4. 功能型函数管道
const double = x => x + x;
const triple = x => 3 * x;
const quadruple = x => 4 * x;
<!--箭头函数写法-->
const pipe = (...funcs) => input => funcs.reduce((arg,fn)=>fn(arg) , input)
<!--return不能忘记,不然multiply9不能得到输出值-->
function pipe(...funcs){
return function(input){
return funcs.reduce(function(arg,fn){
return fn(arg)
},input)
}
}
const multiply6 = pipe(double, triple);
const multiply9 = pipe(triple, triple);
const multiply16 = pipe(quadruple, quadruple);
const multiply24 = pipe(double, triple, quadruple);
console.log(multiply6(6)); // 36
multiply9(9); // 81
multiply16(16); // 256
multiply24(10); // 240
由左向右执行
引出compose函数式编程(由右向左执行)
const add = num => num + 10
const multiple = num =>num * 3
function compose(...funcs){
if (funcs.length === 0 ) arg => arg
if (funcs.length === 1 ) funcs[0]
return funcs.reduce((a,b) => (...args) => a(b(...args)))
}
<!--reduce没赋初始值,a作为数组的第一位,为了从右到左,所以先执行b再行a,args是从result传入的参数-->
let result = compose(multiple,add)
console.log(result(5)) //45
5. 使用 reduce实现map
思路:想map函数的用法,然后传啥函数,map中函数肯定要调用,并且返回一个新的数组;其实这个没用到叠加器的思路,并没有把上一次的回调结果传给下一次回调
function test(){
Array.prototype.myMap = function(callback, thisValue){
return this.reduce(function(mappedArray, value, index, array){
mappedArray[index] = callback.call(thisValue, value, index, array);
return mappedArray;
},[])
}
let result = [1, 2, ,3].myMap(
(currentValue, index, array) => currentValue + index + array.length
);
console.log(result)
// [5, 7, , 10]
}
6.去重算法题
由reduce想到其他去重方法,并进行比较:
- reduce能对数组进行去重,不能对对象
- Set不能对数组和对象都不能去重
- hash={}不能对数组和对象都不能去重
对于包含对象的数组去重改进思路:把元素都变成字符串
test(){
// let arr = [6,6,1,2,2,3,5,5];
// let arr = [6,6,1,2,2,3,[5,5],[5,5]];
let arr = [6,6,1,2,2,3,[5,5],[5,5],{a:1},{b:1}];
let hash = {};
let newArr = [];
方法一:
for(let i=0,len = arr.length;i<len;i++){
if(!hash[arr[i]]){
hash[arr[i]] = true
newArr.push(arr[i])
}
}
// [6,1,2,3,5]
// [6, 6, 1, 2, 2, 3, [5, 5], [5, 5]]
// [6, 6, 1, 2, 2, 3, [5, 5], [5, 5], { a: 1 }, { b: 1 }]
方法二:
newArr = [...new Set(arr)];
// [6,1,2,3,5]
// [6, 1, 2, 3, [5, 5], [5, 5]]
// [6, 1, 2, 3, [5, 5], [5, 5], { a: 1 }, { b: 1 }]
方法三:
let arr = [6,6,1,2,2,3,[5,5],[5,5],{a:1},{a:1},{b:1},"{a:1}","{b:1}"];
arr = arr.reduce(function(total, value, index){
//hash[value] ? '' : hash[value] = true && total.push(value)
<!--或者-->
//if(!hash[value]){
// hash[value] = true ; total.push(value)
// }
<!--使用此办法,对象去重的结果不对,由此想到用JSON.stringify把对象转为字符串-->
//思路:把对象转换成字符串
let strValue = JSON.stringify(value)
if(!hash[strValue]){
hash[strValue] = true ; total.push(value)
}
return total
},[])
console.log(arr)
// [6,1,2,3,5]
// [6, 1, 2, 3, [5, 5]]
// [6, 1, 2, 3, [5, 5], { a: 1 }]
// [6, 1, 2, 3, [5, 5], { a: 1 }, { b: 1 }, "{a:1}", "{b:1}"]
}
总结
涉及到数组,在工作中多考虑使用reduce的高级用法能很快的解决问题。