一、概述
reduce在我们的工作中使用的场景非常广泛,简直可以说是数组方法的万金油,熟练使用reduce绝对是可以让你在提升效率中事半功倍噢~
二、reduce的定义
官方定义:reduce方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
参数:
- callback执行数组中每个值 (如果没有提供 initialValue则第一个值除外)的函数,包含四个参数:
-
accumulator 累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
-
currentValue 数组中正在处理的元素。
-
index 可选 数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。
-
array可选 调用reduce()的数组
-
- initialValue可选 作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
使用例子:
[0, 1, 2, 3, 4].reduce((prev, curr) => {return prev + curr},[]); //01234
[0, 1, 2, 3, 4].reduce((prev, curr) => {return prev + curr},0); // 10
三、reduce的九个常见使用场景
1、数组求和
let arr = [1,2,3,4,5,6,7];
let result = arr.reduce((prev,next)=> prev+next);
console.log(result);
2、遍历累加数组对象里面的值
let arr = [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }];
let result = arr.reduce((prev, next) => prev + next.value, 0);
console.log(result);
3、将二维数组转为一维数组
let arr = [[1,2],[3,4],[5,6]];
let result = arr.reduce((pre,next)=>{
return pre.concat(next)},[]
)
console.log(result) // [1,2,3,4,5,6]
4、计算每个元素出现的次数
let arr = ["a", "b", "a", "c", "a", "d", "c"];
let result = arr.reduce((pre, next) => {
if (next in pre) {
pre[next]++;
} else {
pre[next] = 1;
}
return pre;
}, {});
console.log(result); // {a:3,b:1,c:2,d:1}
5、按指定的属性去对object进行分类
let obj = [
{
name: "ronald",
age: 30
},
{
name: "alice",
age: 31
},
{
name: "tommie",
age: 30
},
];
function getDataBySort(obj, property) {
if (obj.length == 0) return;
return obj.reduce((pre, next) => {
let key = next[property]; //获取指定property的值当做键
if (!pre[key]) {
pre[key] = []; //如果对应的键值不存在,则创建一个,并且值设为空数组。
}
pre[key].push(next); //如果有对应的键值,则直接插入到对应的键名中。
return pre;
}, {});
}
console.log(getDataBySort(obj, "age")); //
//获得的答案是:
{ 30 : [
{name: "ronald",age: 30},
{name:"tommie",age: 30}
],
31: [
{name:'alice',age :31}
]
}
6、善用扩展运算符和initialValue绑定包含在对象数组中的数组
//取出所有的book,并且放在同一个array里
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
}];
let result = friends.reduce((pre,next)=>{
return pre.concat(...next.books);
},[])
console.log(result)
7、数据去重
其实使用es6的新语法更容易实现,使用Array.from(new Set(arr));
let arr = [1,2,3,1,3,4,5,3,4];
console.log(Array.from(new Set(arr)));
使用reduce去实现
let arr = [1,2,3,1,3,4,5,3,4];
let result = arr.reduce((pre,next)=>{
if(!pre.includes(next)){
pre.push(next)
}
return pre
},[])
console.log(result)
8、按顺序调用promise
这里拿的是mdn里的例子
/**
* Runs promises from array of functions that can return promises
* in chained manner
*
* @param {array} arr - promise arr
* @return {Object} promise object
*/
function runPromiseInSequence(arr, input) {
//这里传入的初始参数是一个promise对象,传入了10,所以promiseChain = Promise.resove(10),
//currentFunction 就是p1方法,接收到的参数是10,
//执行完 p1 方法,又返回了一个promise对象,即Promise.resolve(10*5),以此类推。
return arr.reduce(
(promiseChain, currentFunction) => promiseChain.then(currentFunction),
Promise.resolve(input)
);
}
// promise function 1
function p1(a) {
return new Promise((resolve, reject) => {
resolve(a * 5);
});
}
// promise function 2
function p2(a) {
return new Promise((resolve, reject) => {
resolve(a * 2);
});
}
// function 3 - will be wrapped in a resolved promise by .then()
function f3(a) {
return a * 3;
}
// promise function 4
function p4(a) {
return new Promise((resolve, reject) => {
resolve(a * 4);
});
}
const promiseArr = [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10)
.then(console.log); // 1200
9、功能性管道函数
const double = x => x + x;
const triple = x => 3 * x;
const quadruple = x => 4 * x;
//管道函数
const pipe = (...functions) {
return function(input){
return functions.reduce((pre,fn)=> fn(pre),input)
}
}
//上面方法的解释:
// 用展开符...functions把传入的两个方法收集起来成为一个数组;
// 用高阶函数即返回一个函数的方式获取参数;
// return functions.reduce((pre,fn)) 可以理解为[fn1,fn2].reduce((pre,fn));
// 执行fn(pre)获得方法的值返回当做下一个pre的值,继续执行fn(pre)。
const multiply6 = pipe(double, triple);
const multiply9 = pipe(triple, triple);
const multiply16 = pipe(quadruple, quadruple);
const multiply24 = pipe(double, triple, quadruple);
//使用
multiply6(6); // 36
multiply9(9); // 81
multiply16(16); // 256
multiply24(10); // 240
四、重写一个reduce函数
//我们知道一个reduce方法第一个函数一定是一个函数,第二个参数是初始值(可不传)。
Array.prototype.reduceRewrite = function(fn,prev){
//先判断传进来的第一个参数是否为一个函数
if(typeof fn !== 'function'){
return new Error('the first args must be a function');
}
let arr = this;
let i = 0;
//判断一下是否传入了初始值,有传的话,从第0个参数开始循环,不传的话,以第一个参数为初始值。
if(typeof prev == 'undefined'){
i = 1;
prev = arr[0];
}
for(;i<arr.length;i++){
prev = fn(prev,arr[i],i,arr);
}
return prev;
}
五、用reduce重写一个map函数
先看看map函数的用法,map会传入一个回调函数当做参数做循环,最后返回的是一个数组。
arr.map(function(item,idx,arr){
item //数组的每一项
idx // 当前项的索引
arr // 当前遍历的数组
this // 函数体内的 this 为 callbackThis参数,
// 如果不指定或者指定为 null和 undefined,则 this指向全局对象
},callbackThis)
再复习下reduce的用法
[...].reduce((pre,next,idx,arr)=>{
pre // 前面累加的值
next // 数组中正在处理的数
idx // 当前项的索引
arr // 当前遍历的数组
},initValue)
这个时候我们开始重写map方法。
Array.prototype.reducerMap = function (fn, callbackThis) {
//如果this没有指定默认指向window,避免报错
let callThis = callbackThis || window;
//因为map最终是返回一个数组,所以先初始化一个数组
let res = [];
//这里的this指向arr数组,然后调用reduce方法
this.reduce((pre, next, ids, arr) => {
//把reduce的四个参数用call方法绑定回fn里插入数组,因为用了call方法,所以会执行里面的函数,
//即下面传进来的(item,index)=>{return item > 3}的这个方法,
//最后把每一项的执行结果push进res中
return res.push(fn.call(callThis, next, ids, arr));
}, []);
return res;
};
let arr = [1, 2, 3, 4, 5];
let Arr = arr.reducerMap((item, index) => {
return item > 3;
});
console.log(Arr); //[false,false,false,true,true]
上面就是reduce的常用场景啦,其实它还有很多场景值得我们去探索,善用这个方法,一定能提升我们的编程能力,继续加油,天道酬勤。