日常工作中对数组中的 API 使用频率还是挺高的,唯独 reduce()
,在阅读相关介绍时,一直很难理解,只记得数组求和和去重,能够用到它,其他地方怎么用,就无法灵活变通了。今天索性来认真地总结一下reduce()
的用法,学会在更多的地方使用它,提升代码水平和开发效率。
一、基本语法
arr.reduce(function(prev,cur,index,arr){...},init);
其中参数从左到右依次是prev
、cur
、index
、arr
、init
。
参数 | 含义 | 必需 |
---|---|---|
prev | 上一次调用回调时的返回值,或者初始值init | 是 |
cur | 当前正在处理的数组元素 | 是 |
index | 当前正在处理的数组元素的索引,若提供 init 值,则index 从0 开始,否则索引从1 开始 | 可选 |
arr | 原数组 | 可选 |
init | 初始值 | 可选 |
5个参数看上去十分复杂,然则真正常用的参数只有两个即prev
和cur
,可以通过具体示例来学习一下如何使用它。
二、常用示例
首先提供一个元素都是Number
类型的原始数组:const origin = [1,2,3,4,5]
1、求该数组各项之和
const arr_one = [1,2,3,4,5];
let sum = arr_one.reduce((prev,cur) => prev+cur,0);
console.log(sum); // 15
分析:由于传入了初始值0,所以开始时 prev = 0,cur 是数组第1项 1,相加(0+1)后的返回值为 1 作为下一轮回调的 prev 的值,即 prev = 1,然后在继续与下一个数组项 cur = 2 相加,以此类推,直到完成所有数组项的和并返回。
prev = init = 0, cur = arr[0] = 1, index=0, prev + cur = 1;
prev = 1,cur = 2,index = 1, prev + cur = 3; prev = 3,cur = 3,index = 2, prev + cur = 6;
prev = 6,cur = 4,index = 3, prev + cur = 10; prev = 10,cur = 5,index = 4,prev + cur = 15;
2、求数组项的最大值
const arr_two = [6,4,2,3,4,5];
// 方法1:
let max = arr_two.reduce((prev,cur) => prev>cur?prev:cur);
// 方法2:
// let max = arr_two.reduce((prev,cur) => Math.max(prev,cur));
console.log(max); // 6
由于未传入初始值 init
,prev
为数组第1项6,cur
为数组第2项1,取两个值的最大值后继续进行下一轮回调。
3、数组进行去重操作
const arr_three = [7,8,5,4,4,5,6,7,8];
let new_arr = arr_three.reduce((prev,cur) => {
//当前元素在数组中不存在,即push到prev中
prev.indexOf(cur) === -1 && prev.push(cur);
return prev;
},[])
console.log(new_arr ); //[7, 8, 5, 4, 6]
基本实现原理:
(1)、初始化一个空数组即 prev = init = []
(2)、将需要做去重处理的数组中的第1项在初始化数组中查找,找不到(空数组中肯定找不到),添加到初始数组 prev 中
(3)、将需要做去重处理的数组中的第2项在初始化数组中查找,找不到(空数组中肯定找不到),添加到初始数组 prev 中
(4)、以此类推,…
(5)、将需要做去重处理的数组中的第n项在初始化数组中查找,找不到(空数组中肯定找不到),添加到初始数组 prev 中
(6)、最后返回初始化数组即 prev
三、其他用法
1.reduceRight()
该方法用法与reduce()
相同,只是遍历的顺序相反,它是从数组的最后一项开始,向前遍历到第1项。
2.forEach()、map() 、every()、 some()和 filter()
reduce()
是数组的归并方法,与forEach()
、map()
、filter()
等迭代方法一样都会对数组的每一项进行遍历,只是
reduce()
可同时将前面数组项遍历产生的结果与当前遍历项进行运算,这是其他迭代方法所不具备的特性。
四.进阶用法
(1) 数组对象中的用法
需求1:生成 “老大、老二和老三”
const objArr = [{name:"老大"},{name:"老二"},{name:"老三"}];
const res = objArr.reduce((prev,cur,index,arr)=>{
if(index === 0) { return cur.name }
else if(index === arr.length-1) {
return prev + '和' + cur.name
} else{ return prev+'、'+cur.name; }
},'');
console.log(res); // 老大、老二和老三
(2) 字符串中字母出现次数
需求2:求字符串中字母出现次数
const str = 'sfhjasfjgfasjuwqrqadqeiqsajsdaiwqdaklldflas-cmxzmnha';
const res = str.split('').reduce((prev,cur) => {
console.log('prev = ',prev);
console.log('cur =',cur);
prev[cur] ? prev[cur]++ :prev[cur]=1;
return prev
},{});
console.log(res) ;
// { "s": 6, "f": 4, "h": 2, "j": 4, "a": 8, "g": 1, "u": 1, "w": 2, "q": 5, "r": 1, "d": 4, "e": 1, "i": 2, "k": 1, "l": 3, "-": 1, "c": 1, "m": 2, "x": 1, "z": 1, "n": 1 }
(3) 数组转数组
需求3:按照一定的规则转成数组,例如:每个值的平方
const arr_origin = [1,2,3,4,5];
const arr_sqr = arr_origin.reduce((prev,cur) => {
prev.push(cur*cur);
return prev;
},[]);
console.log(arr_sqr); // [1, 4, 9, 16, 25]
(4) 数组转对象
需求4:按照id
取出 stream
const streams = [{name:'tech',id:1},{name:'design',id:2},{name:'math',id:3}];
const obj = streams.reduce((prev,cur) => {
prev[cur.id] = cur;
return prev;
},{});
console.log(obj);
// { "1": { "name": "tech", "id": 1 }, "2": { "name": "design", "id": 2 }, "3": { "name": "math", "id": 3 } }
五、高阶用法
(1)多维叠加执行操作
需求1:各科成绩占比重不一样,求结果
const result = [
{subject:'math',score:80},
{subject:'chinese',score:90},
{subject:'english',score:100}
];
const dis = {math:0.5, chinese:0.3, english:0.2};
const res = result.reduce((prev,cur) => dis[cur.subject]*cur.score+ prev,0);
console.log(res); // 80*0.5 + 90*0.3 + 100*0.2 = 87
需求2:商品对应不同国家汇率不同,求总价格
const prices = [{price:23}, {price:45}, {price:56}];
const rates = {us:'6.5', eu:'7.5'};
const initState = {usTotal:0,euTotal:0};
const res = prices.reduce((prev1,cur1)=>Object.keys(rates).reduce((prev2,cur2)=>{
console.log(prev1,cur1,prev2,cur2);
prev1[`${cur2}Total`] += cur1.price * rates[cur2];
return prev1;
},{}), initState);
const manageReducers = () => {
return (state,item) => Object.keys(rates).reduce((nextState,key) => {
state[`${key}Total`] += item.price * rates[key];
return state;
},{});
};
const res1 = prices.reduce(manageReducers(),initState);
console.log(res1);// {usTotal: 1612, euTotal: 1860}
(2)扁平一个数组
const arr = [[1, 2], [8], [3, 4, 9], [5, 6, 10]];
const res = arr.reduce((x,y) => x.concat(y),[]);
console.log(res);
(3)对象数组去重
const hash = {};
chatlists = chatlists.reduce((obj,next:Object) => {
const hashId = `${next.topic}_${next.stream_id}`;
if(!hash[hashId]) {
hash[ `${next.topic}_${next.stream_id}`] = true;
obj.push(next);
}
return obj;
},[]);
(4) redux compose 源码实现
function compose(...funs) {
if(funs.length === 0) {
return arg => arg;
}
if(funs.length === 1) {
return funs[0];
}
return funs.reduce((a,b) => (...arg) => a(b(...arg)));
}