reduce的理解和使用技巧

966 阅读5分钟

语法

写法:
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的高级用法能很快的解决问题。