JS中reduce()的用法

日常工作中对数组中的 API 使用频率还是挺高的,唯独 reduce(),在阅读相关介绍时,一直很难理解,只记得数组求和和去重,能够用到它,其他地方怎么用,就无法灵活变通了。今天索性来认真地总结一下reduce()的用法,学会在更多的地方使用它,提升代码水平和开发效率。

一、基本语法

arr.reduce(function(prev,cur,index,arr){...},init);

其中参数从左到右依次是prevcurindexarrinit

参数含义必需
prev上一次调用回调时的返回值,或者初始值init
cur当前正在处理的数组元素
index当前正在处理的数组元素的索引,若提供 init 值,则index0开始,否则索引从1 开始可选
arr原数组可选
init初始值可选

5个参数看上去十分复杂,然则真正常用的参数只有两个即prevcur,可以通过具体示例来学习一下如何使用它。

二、常用示例

首先提供一个元素都是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

由于未传入初始值 initprev 为数组第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)));
}