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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351