记一下Array中那巧妙的reduce方法

reduce这个看似平常的东西,实质上却是一个很有用的方法,很多人更多的是把它当作一个求和的工具,但其实它有更多的用法,具体要看我们怎么去把它的用法挖掘出来,接下来慢慢说一下。

reduce()是JavaScript  Array数组的归并方法,与forEach、map、some、every等迭代方法一样都会对数组每一项进行遍历,但是reduce可以同时将前面数组项遍历产生的结果和当前元素进行运算,这一点是其它迭代方法做不到的,也因此使它拥有不一样的能力。

一、reduce 语法

arr.reduce(function(prev, cur, index, arr) {
  ......
}, init)
  • arr  表示原数组;

  • prev  表示上一次调用回调时返回的值,或者初始值init,未指定初始值时值为数组第一个元素,而此时cur为数组第二个元素;

  • cur  表示当前正在处理的数组元素;

  • index  表示当前正在处理的元素的索引,若指定init,则index为0,若无指定init,则index为1;

  • init  表示初始值,对类型无要求。

看起来几个变量似乎还挺麻烦的,但实际上要记的主要有两个:prev和cur,还有一个要注意的初始值init。

二、reduce 用例

接下来说一下reduce的使用事例,当然也不仅仅只有以下的使用方法,只不过以下比较常用,具体更高逼格的可以在需要时扩展实现。

1、求和

首先当然是我们都熟知的求和,这个比较简单

var arr = [12, 42, 12, 61, 73, 19]
var sum = arr.reduce(function(prev, cur){
  return prev + cur
}, 0)
console.log(sum) // 219

因为传入了初始值0,所以刚开始prev为0,cur为数组的第一项12,相加后值为12并将该值返回作为第二次相加的prev,然后又与数组第二项相加,再返回,之后依次进行,最后得到结果219。

这个小意思呐,我们再提高下难度,我们要求对象数组中某一属性的和

var goods = [
  {type: 'slipper', price: 24}, 
  {type: 'bowl', price: 11},
  {type: 'chopsticks', price: 5},
  {type: 'plate', price: 9}
]

var sum = goods.reduce(function(prev, cur) {
  return prev + cur.price
}, 0)

console.log(sum) // 49

道理还是一样,但是别忘了给个初始值0,不然结果就是[object Object]1159,因为此时prev是第一项,是一个对象,之后进行类型转换,此时调用valueOf,返回的对象本身不是可操作值,再次调用toString返回对象字符串[object Object],之后的就成了字符串的拼接了。

初始值还可以是一个对象

var sum = goods.reduce(function(prev, cur) {
  prev.sum = prev.sum + cur.price
  return prev
}, { sum: 0})

console.log(sum) // { sum: 49 }

可以看到,对初始值的类型其实没有要求,只要在函数中的运算操作正确,且返回运算后的prev就可以了。

以上求和都是单个维度的叠加,现在我们看一下多个维度的叠加,也就是说其中涉及到多个属性的求和而不是简简单单的求一个属性之和。现在我们改一下上面用例要求,假如有个各种活动时候的商品折扣表,现在要计算在每种折扣下的商品价格总和。

var goods = [
  {type: 'slipper', price: 24},
  {type: 'bowl', price: 11},
  {type: 'chopsticks', price: 5},
  {type: 'plate', price: 9}
]

var discount = {
  dis1: 0.6,
  dis2: 0.7,
  dis3: 0.8
}

var initialState = {
  dis1Total: 0,
  dis2Total: 0,
  dis3Total: 0
}

var sum = goods.reduce(function(outerPrev, outerCur) {
  return Object.keys(discount).reduce(function(innerPrev, innerCur) {
    outerPrev[`${innerCur}Total`] += outerCur.price*discount[innerCur]
    return outerPrev
  }, {})
}, initialState )

console.log(sum) // {dis1Total: 29.4, dis2Total: 34.3, dis3Total: 39.2}

其实有点循环体中加一个循环的感觉,看懂了还是蛮好理解的。

2、求最值

reduce还可以用来求取数字类型数组的最大或者最小值,以下以最大值为例

var arr = [12, 42, 12, 61, 73, 19]
var max = arr.reduce((prev, cur) => {
  return prev > cur ? prev : cur
})
console.log(max) // 73

由于没有提供初始值,所以prev是第一个元素12,在第一轮比较的时候12<42,所以第一轮后prev的值为42并返回,之后依次比较,得到最大值73。

3、去重复

reduce还可以用来去除数组中的重复元素

var arr = [12, 42, 12, 61, 73, 12, 42, 13, 19]
var result = arr.reduce(function(prev, cur){
  prev.indexOf(cur) === -1 ? && prev.push(cur)
  return prev
},[])

console.log(result) // [12, 42, 61, 73, 13, 19]

以上提供了初始值是一个空数组,每次判断当前元素是否在数组中,在的话则不操作,原数组返回,不在的话将元素放入prev中,最后得到不重复的数组。

同样的道理还可以用在字符串中,去除字符串中重复字符,得到新的不重复字符的字符串

var str = 'asdaadasfdsdfaderhrrtjsfb'
var result = str.split('').reduce(function(prev, cur) {
  prev += prev.indexOf(cur) === -1 ? cur : ''
  return prev
}, '')

console.log(result) // asdferhtjb

当然也可以实现对象数组的去重

var goods = [{type: 'plate', No: 123}, {type: 'bowl', No: 321}, {type: 'plate', No: 123}]

var result = goods.reduce((prev, cur) => {
  if(!prev.some(item => item.No === cur.No)) {
    prev.push(cur)
  }
  return prev
}, [])

console.log(result) // [{type: plate, No: 123}, {type: bowl, No: 321}]

4、求次数

利用reduce可将前面遍历结果和下一项进行运算的特性,我们还可以求元素或者字符在数组或者字符串中出现的次数,以下以求字符在字符串中出现次数为例

var str = 'asfasdasghsfadasdfsdgasdafgsada'

var result = str.split('').reduce((prev, cur) => {
  prev[cur] ? prev[cur]++ : prev[cur] = 1
  return prev
}, {})

console.log(result) // {a: 9, s: 8, f: 4, d: 6, g: 3, h: 1, s: 8 }

求元素在数组中出现次数与上面同理。

5、类型转换

数组转对象

var goods = [{type: 'plate', No: 123}, {type: 'bowl', No: 321}]

var result = goods.reduce((prev, cur) => {
  !prev[cur.No] && (prev[cur.No] = cur)
  return prev
}, {})

console.log(result ) // {123: {type: "plate", No: 123}, 321: {type: "bowl", No: 321}}

对象转数组

var obj = {123: {type: "plate", No: 123}, 321: {type: "bowl", No: 321}}

var result = Object.keys(obj).reduce((prev, cur) => {
  prev.push(obj[cur])
  return prev
}, [])

console.log(result) // [{type: "plate", No: 123}, {type: "bowl", No: 321}}]

当然这点看起来有点鸡肋,但这里也只是陈述它具备有这种实现能力。


OK,到这里基本上所熟知的关于reduce的常用用法就如上所列,而实质上只要学会变通,我们也可以开发出更多的使用技巧,只是成本的高低和做法的优劣问题,JS千变万化,我们可以有更多的选择。

以下是我的公众号,专门推送前端的一些总结以及其它知识,有兴趣的关注一下呗。

“菜鸟札记”公众号

猜你喜欢

转载自blog.csdn.net/HU_YEWEN/article/details/117288155