关于 JS 中的 reduce 函数的常见用法

一、前言

如题,JS的数组中有许多十分常用的内置函数,它们大多被封装在 Array.prototype 原型上,比如 map, concat, filter, find, push, splice 等等常用函数。今天笔者想要介绍下 Array.prototype 上的reduce 函数。原因是相比于前面列举的那些函数来说, reduce 函数不是那么常见却又非常实用,不管是在面试还是在实际开发中都会见到。

二、reduce 函数用法简介

reduce,在英文中有“归纳为……”的意思,它出现的本意也是为了实现一种归纳效果。

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

其函数参数为

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

在上述表述中,"[ ]" 中括号中的参数是可选的,也就是说,reduce的参数一般有两个,callback 回调函数和 initialValue 初值,其中 initialValue 不是必填的。

callback 函数是作为一个累加器存在的,其中 accumulator 是 callback 函数累计处理返回的值,在实际运行中 accumulator 是上一次 callback 函数运行完成后返回的累计值。而 currentValue 表示当前数组(arr 数组)下标中的元素。

index 代表 arr 数组的当前下标。

array 则是对 arr 数组的引用。

其中,index 和 array 都不是必填的。

reduce 函数最终回返回累计处理的结果,也就是 accumulator 最终的值,和 map,filter 等函数一样,reduce 函数不会改变原数组 arr 的值。

三、reduce 函数用法举例

1. 作为累加器使用

首先,当不传 initialValue 且调用 reduce 的数组为空时,函数会报错:

[].reduce((arr, cur) => arr + cur)

// Uncaught TypeError: Reduce of empty array with no initial value

 当传入 initialValue 且原数组为空时,reduce 会直接返回 initialValue 的值:

[].reduce((arr, cur) => arr + cur, 3) // 3

当原数组不为空且 initialValue 也有值时,initialValue 会作为累加器的第一个累加值参与到 callback 函数运行中:

const a = [1, 3, 5, 7, 9]

a.reduce((acc, cur) => {
  console.log('acc', acc)
  return acc + cur
}, 10)


// 35
// acc 10
// acc 11
// acc 14
// acc 19
// acc 26

而当原数组不为空且不传入 initialValue 时,acc 的初始值为原数组的第一个元素。按上例则最终返回值为 25。

这里有一个值得注意的点就是为什么需要有 array 这个参数,毕竟 reduce 函数不会改变 arr 数组的内容,考虑下面情况:

[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array){
  console.log('array', array)
  return accumulator + currentValue;
})

// array [0, 1, 2, 3, 4]
// array [0, 1, 2, 3, 4]
// array [0, 1, 2, 3, 4]
// array [0, 1, 2, 3, 4]
// 10

此时 array 参数是有作用的。

2. 数组扁平化

记得在一次面试中,有一个面试官问笔者如何实现数组扁平化,作为 JS 来说其实是有现成的 flat 函数可以直接实现数组扁平化的,而如果需要自己手写的话,办法也是比较多的,比如说利用 reduce 函数实现,奇怪的是当时笔者介绍可用 reduce 函数实现数组扁平化时,面试官觉得很诧异,似乎既没听过也推断不出有这种用法来…所以这里也是告诉我们有时候出去面试,面试官的水平不一定很高,甚至他不是一个前端开发人员,对 JS 不是很熟悉也是很常有的事情。

下面我们介绍下 reduce 的数组扁平化版本:

function Flat(arr = []) {
    return arr.reduce((t, v) => t.concat(Array.isArray(v) ? Flat(v) : v), [])
}

const arr = [0, 1, [2, 3], [4, 5, [6, 7]], [8, [9, 10, [11, 12]]]];
Flat(arr); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

这里利用数组的 concat 函数和递归来将多维数组中的每一个元素都加入到累计器 t 中。 

 3. 数组成员个数统计

这个问题偶尔在一些业务中会使用到,比如说统计一个文本中的指定单词出现的次数,这时我们可以先用 split 函数将字符串转换为数组,然后对数组调用 reduce 处理:

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;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

4. 数组去重

数组去重也是经常会在笔面试中出现的高频问题,在实际工作中,一般我们会直接利用 ES6 所支持的 Set 数据结构配合 Array.from 方法直接去重:

const uniqueArray = Array.from(new Set(myArray));

在这里先将 myArray 数组转换成 Set 格式,利用 Set 格式自动去重的特性去除重复元素,再使用 Array.from 将 Set 转回 Array。

如果不借助 Set 结构,也可以有很多种方法实现去重,比如说:

function unique(arr){            
  for(var i=0; i<arr.length; i++) {
    for(var j=i+1; j<arr.length; j++) {
      if(arr[i]==arr[j]) {         //第一个等同于第二个,splice方法删除第二个
        arr.splice(j,1);
          j--;
      }
   }
}
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]     //NaN和{}没有去重,两个null直接消失了

利用双重 for 循环进行去重,该方法思路类似于冒泡排序,即依次拿出某个元素来与数组中的其他元素做比较,当发现其与数组中的其他元素相等时,使用 splice 方法删除掉这个相等的“其他元素”。

而实际上用 reduce 来实现数组去重是很合适的:

let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd']
let myOrderedArray = myArray.reduce(function (accumulator, currentValue) {
  if (accumulator.indexOf(currentValue) === -1) {
    accumulator.push(currentValue)
  }
  return accumulator
}, [])

console.log(myOrderedArray)

该方法简介易懂,即遍历数组,若当前某元素不存在于累加数组中,则将其 push 到累积数组内。

四、总结

reduce 是 JS Array 自带方法中非常强大的一个函数,其本意为归纳一个数组,基于该立意进行的功能实现不仅可以减少代码量而且增加了代码可读性,但是其作用却远不止于此。

除了以上几种比较常见的 reduce 使用场景外,实际上 reduce 还可以用在很多其他场景上,几乎涵盖了所有有关遍历数组的用法中,如:实现 map, filter, some, every的效果,求数组最大最小值,按属性分类等等。 当然,在这些场景中,可以用 reduce 实现功能也可以用其他函数及方法实现相应功能,此时笔者认为并不一定要十分追求利用 reduce 实现,更多的时候,我们要立足于代码的可读性及简洁性去选用相应的方法实现功能。

参考:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

https://juejin.cn/post/6844904063729926152

https://segmentfault.com/a/1190000016418021?utm_source=tag-newest

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
reduce函数JavaScript数组的一个高阶函数,它可以将数组的所有元素按照指定的规则进行累加或转换。它接收一个回调函数作为参数,该回调函数可以在每次迭代对累加值和当前元素进行操作。 回调函数接收四个参数:累加值(也称为累加器)、当前元素、当前索引和整个数组。它可以返回一个新的累加值,供下一次迭代使用。 reduce函数有两个可选参数:初始值和上下文对象。初始值是可选的,如果不传递初始值,则将使用数组的第一个元素作为初始值,并从第二个元素开始迭代。上下文对象用于指定回调函数的this值。 下面是reduce函数的基本用法示例: ```javascript const arr = [1, 2, 3, 4, 5]; // 对数组进行累加求和 const sum = arr.reduce((accumulator, currentValue) => { return accumulator + currentValue; }, 0); // 初始值为0 console.log(sum); // 输出: 15 ``` 在上面的例子reduce函数数组arr的元素依次相加,并将结果保存在累加值`accumulator`。初始值为0,所以第一次迭代时`accumulator`的值为0,`currentValue`的值为1,返回结果1。紧接着,第二次迭代时`accumulator`的值为1,`currentValue`的值为2,返回结果3。以此类推,最终得到累加值15。 除了累加求和,reduce函数还可以用于累乘、字符串拼接、数组元素去重等操作。通过在回调函数编写不同的逻辑,我们可以实现各种灵活的功能。 希望能帮到你!如果有更多问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值