javascript reduce方法详解,带一道常考面试题实例

2,267 阅读5分钟

reduce是什么

mozilla
reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值

白话来说
reduce方法就是用于操作数组,执行时需要提供一个函数参数用于处理数组中的每一项元素,方法执行完毕后会返回一个值。
也就是说redux是处理数组的方法,并且处理完毕后是有返回值。

语法

reduce() 接收一个函数作为累加器(这个函数会操作数组中的每一个元素),数组中的每个值(从左到右)开始缩减,最终计算为一个值。
除了接受一个函数外,还接受一个初期值initialValue,这个值可以是数字、对象等。

const reducer = function(accumulator, currentValue, currentIndex, array){ /** some code... **/ };
const initialValue = {};//也可以不传
array.reduce(reducer, initialValue)

函数参数(reducer)

const reducer = function(accumulator, currentValue, currentIndex, array){ /** some code... **/ };
函数参数(reducer) 四个形参说明:

1. accumulator 累计器

累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue。也就是说下一次执行reducer函数时获取到的accumulator参数是上一次reducer函数执行返回的,如果是第一次执行的话将会采用initialValue,initialValue不存在的话将会采用数组的第一个元素。

2. currentValue 当前值

这个就比较好理解了,就是正在处理的那一项数组元素

3. currentIndex 当前索引

正在处理的那一项数组元素的索引

4. array 数组

调用reduce()的数组,就是被操作的数组

干饭了(实例)

1. 计算数组各个元素相加的和

const numbers = [10, 20, 30]; 

//这里只需要用到累计器和当前处理值两个形参
//getSum执行后返回上一个值和当前被处理的数组元素相加,然后又返回相加的值,就这样实现了累加
//这个方法会被执行三次,分别输出:10、30、60
const getSum = (total, num)=> {
    const next = total + num;
    console.log(next)
    return next;
}
const sum = numbers.reduce(getSum, 0)
console.log(sum); //输出:60

是不是很简单,其实就像是循环了一下数组,每次循环会执行一个你写的函数,函数会返回一个值,这个值你在下次循环时候是可能获取到的,你可以使用这个值继续做任何事。第一次这个值由初始值参数充当。

2. 写一个有点难度的逻辑来处理一些事情(百度可能找不到或者不好找的案例和解说)

show code

这八行代码实现了什么🤔?

咋一看使用的方法还挺多的,但仔细一瞅,害,无非就是Object.keys、.map、.split、reduce这四个方法而已啦~。 先看需求吧,不然各位同志可能看完都没时间干饭了。


假如有一道面试题:
//有如下对象字面量数据
const foo1 = {
  'A':1,
  'B.A':2,
  'B.B':4,
  'CC.D.E':3,
  'CC.D.F':5
}

//写一个方法将其格式化化为:
const foo1 =  {
    "A": 1,
    "B": {
        "A": 2,
        "B": 4
    },
    "CC": {
        "D": {
            "E": 3,
            "F": 5
        }
    }
}

各位干饭人很快就能看明白,不就是要将 用 . 符号连接的字符串拆开,然后变成嵌套对象吗?一般我们解决这种事大多数选择是使用递归。其实 使用reduce性能会更好,代码也会更简洁。

上述代码分析

foo1 对象的每个key都是字符串并且使用 . 符号连接,并且值是一个数字,也就是说不管左边k有多少层,但是右边的值只有一个,那就只是需要把左边拆开在组合为一个对象,然后把右边的值给到对象的最底层

  • 代码分析
  1. Object.keys(foo1)

    第一步使用Object.keys取出目标对象的所有key,将会得到这样的结果:

   ["A", "A.B", "B.B", "CC.D.E", "CC.D.F"] 
  1. Object.keys(foo1).map(item=>item.split('.')) 第一步使用split拆解key,拿到一个二维数组,将会得到这样的结果:
    [        ["A"], 
        ["A","B"],
        ["B","B"], 
        ["CC","D"."E"]", 
        ["CC","D","F"]
    ]
  1. 这时候需要对二维数组进行操作并且返回一个对象,也就是返回一个我们所要的对象,这时候我们可以去到foo1每个属性的值,也就是右边的数字。也可以拿到foo1每一个属性key的一个数组,我们只需要将这个key嵌套为一个对象,并把值赋值给对象的最底层即可。

    该例子一定需要有耐心的去理解🤔

    Object.keys(foo1).map(item=>item.split('.')).reduce((pre, cur)=>{ 
      
      //pre在第一次执行时候是一个空对象 {},第二次执行是携带了上一次处理过了的对象
      //cur是一个数组,需要循环数组的每一项数据,并且循环时候需要记录上一次循环做的操作,于是还是得使用reduce
      
      cur.reduce((cPre, cCur, cIndex)=>{ 
      	//cPre是一个对象,第一次执行和pre是一样的,第二次为上一次执行时返回的对象
        //cCur是一个字符串,是具体的对象的某个层级的属性名字,
        
        //这里判断如果提供的累计器(也就是上一次处理的对象)的[当前属性名字]不存在的时候那就让其等于一个空对象{}
        if(!cPre[cCur]) cPre[cCur] = {};
        
        //如果当前处理的数组元素的索引值等于当前数组的最后一个元素的索引值的时候 就 进行赋值,这时候也就说已经拿到了最底层的那个属性了
        if(cIndex === cur.length - 1) cPre[cCur] = foo1[cur.join('.')];
        
        //返回一个对象  注意这个对象如果是空的那应该在上上不骤让其等于一个 {} 所以在下一次的循环时,又会周而复始的进行上述步骤
        return cPre[cCur]
      }, pre) 
      
      //这里返回出去已经处理好的属性
      return  pre
    }, {})

参考文献: mozilla - reduce(这里面有一些其他的使用实例很值得参考)