reduce方法实探

一、定义

reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。也就是说可以在给定初值的情况下,遍历数组中所有元素,执行reducer函数,并返回结果。

二、用法
arr.reduce(callback[, initialValue])
  • callback 接收4个参数:

    您的 reducer 函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。

    reducer = (acc, cur[, idx[, src]]) => {
        // function
      // 返回函数累计处理的结果
      return acc
    }
    
    • Accumulator (acc) (累计器)

      累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。

      • 传入initialValue时,则acc=initialvalue,遍历从第0个元素开始
      • 未传入initialValue时,则acc=arr[0],遍历从第1个元素开始
      let arr = [1,2,3,4,5]
      arr.reduce((acc, cur, idx) => {
          // 未传入初值,遍历从第1个元素开始,第一次时acc=1,cur=2
          // 1,3,6,10,15
          console.log(acc)
          return acc += cur
      })
      arr.reduce((acc, cur, idx) => {
          // 传入初值0,遍历从第1个元素开始,第一次遍历时acc=0,cur=1
          // 0,1,3,6,10,15
          console.log(acc)
          return acc += cur
      }, 0)
      
    • Current Value (cur) (当前值)

      数组中正在处理的元素。

      • 传入initialValue时,则cur=arr[0],遍历从第0个元素开始
      • 未传入initialValue时,则crr=arr[1],遍历从第1个元素开始
      let arr = [1,2,3,4,5]
      arr.reduce((acc, cur, idx) => {
          // 未传入初值,遍历从第1个元素开始
          // 2,3,4,5
          console.log(cur)
      })
      arr.reduce((acc, cur, idx) => {
          // 传入初值0,遍历从第1个元素开始
          // 1,2,3,4,5
          console.log(cur)
      }, 0)
      
  • Current Index (idx) (当前索引)[可选]

    数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则为1

    let arr = [1,2,3,4,5]
    arr.reduce((acc, cur, idx) => {
        // 未传入初值,遍历从第1个元素开始
        // 1,2,3,4
        console.log(idx)
    })
    arr.reduce((acc, cur, idx) => {
        // 传入初值0,遍历从第1个元素开始
        // 0,1,2,3,4
        console.log(idx)
    }, 0)
    
  • Source Array (src) (源数组)[可选]

    调用reduce()的数组

  • initialValue 初始值

    作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用reduce 将报错

三、注意
  • reduce为数组中的每一个元素依次执行callback函数,不包括数组中被删除或从未被赋值的元素

  • let arr = [1,2,,4,5] // undefined、null都视为被赋值
    arr.reduce((acc, cur, idx) => {
        // 未传入初值
        // 1,3,4
        console.log(idx)
    })
    arr.reduce((acc, cur, idx) => {
        // 0,1,3,4
        console.log(idx)
    }, 0)
    
  • 在没有初始值的空数组上调用reduce将报错

  • [].reduce((acc, cur, idx) => {
          // callback未执行,但有初始值,故返回初始值
        console.log(idx)
    }, 0)
    [].reduce((acc, cur, idx) => {
        // callback未执行,没有初始值,报错
        console.log(idx)
    })
    // Uncaught TypeError: Reduce of empty array with no initial value
    
  • 未传入初始值,则callback执行arr.length-1次,因为遍历从第1个元素遍历,acc=arr[0];传入初始值,则callback执行arr.length次,因为遍历从第0个元素遍历,acc=initialValue

四、拓展
1.initialValue为对象时,arr该如何变化?
  • 传入初值

acc === initValue,故任何修改都不会影响arr

let arr = [{i: 1}, {i: 2}, {i: 3}]
let initialValue = {i: 0}
arr.reduce((acc, cur, idx) => {
    // 直接修改acc, 即操作initialValue
    acc.i += cur.i
    return acc
}, initialValue)
// arr = [{i: 1}, {i: 2}, {i:3}]
// initialValue = {i:6}
  • 未传入初值

acc === arr[0],对acc做任何运算,即相当于操作arr[0]

let arr = [{i: 1}, {i: 2}, {i: 3}]
arr.reduce((acc, cur, idx) => {
    // 直接修改acc
    acc.i += cur.i
    return acc
})
// arr = [{i: 6}, {i: 2}, {i:3}]
// {i:6}

解决思路:不直接修改acc

let arr = [{i: 1}, {i: 2}, {i: 3}]
arr.reduce((acc, cur, idx) => {
    // 返回新对象
    return {i: acc.i + cur.i}
})
// arr = [{i: 1}, {i: 2}, {i:3}]
// {i:6}

结论:提供初始值initialValue更安全!

五、与其他方法进行对比

数组常用的遍历有forforEachmapreducefindsomeevery

1.遍历规则
  • 已删除的项

都跳过已删除的项

  • 未初始化的项

除了find,其他的遍历都会跳过未初始化的项

let a = [1,null,,4,undefined,5,6]
a.find(item => {
    console.log(item)
})
// 输出1,null,undefined,4,undefined,5,6
2.返回值
  • for为代码块,无返回值
  • forEach 无返回值
  • map 返回一个新数组,每个元素都是回调函数的结果。
  • reduce 返回函数累计处理的结果,即acc
  • find 返回数组中第一个满足所提供测试函数的元素的值,否则返回 undefined
  • some 如果回调函数返回至少一个数组元素的truthy值,则返回true;否则为false
  • every 如果回调函数的每一次返回都为truthy值,返回true ,否则返回false
六、应用场景
1.求累加和
let arr = [{i: 1}, {i: 2}, {i: 3}]
let initialValue = 0
initialValue = arr.reduce((acc, cur, idx) => {
    return acc + cur.i
}, initialValue)
// initialValue = 6
2.将二维数组转换为一维数组
let arr = [[0,1],[2,3],[4,5]]
let initialValue = []
initialValue = arr.reduce((acc, cur, idx) => {
    return [...acc,...cur]
}, initialValue)
// initialValue = [0,1,2,3,4,5]
3.计算数组中每个元素出现的次数
let arr = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']
let initialValue = {}
initialValue = arr.reduce((acc, cur) => {
    acc[cur] = (acc[cur] || 0 ) + 1
    return acc
}, initialValue )
// initialValue = {Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}
4.按属性对object分类
let arr = [{name: 'a', age: 18}, {name: 'b', age: 19}, {name: 'c', age: 18}, {name: 'd', age: 20}]
let groupBy = (objectArray, property) => {
    return objectArray.reduce((acc, cur) => {
        // 当前元素存在该属性
        if (property in cur) {
            acc[cur[property]] = acc[cur[property]] || []
            acc[cur[property]].push(cur)
        }
        return acc
    }, {})
}
// 根据age属性对arr分类
groupBy(arr, 'age')
/*
{
    18: [{name: 'a', age: 18}, {name: 'c', age: 18}],
    19: [{name: 'b', age: 19}],
    20: [{name: 'd', age: 20}]
}
*/
5.数组去重
let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4]
arr.sort().reduce((acc, cur) => {
    if (!acc.includes(cur)) {
        acc.push(cur)
    }
    return acc
}, [])
// [1,2,3,4,5]

参考网址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。