JS数组Reduce方法详解
reduce方法将会对数组元素从左到右依次执行reducer函数,然后返回一个累计的值。
reduce函数的参数
reduce函数接收两个参数,第一个参数是回调函数reducer ,第二个参数是初始值。reducer
函数接收四个参数
- Accumulator:MDN上解释为累计器,但我觉得不恰当,按我的理解它应该是截至当前元素,之前所有的数组元素被reducer函数处理累计的结果
- current:当前被执行的数组元素
- currentIndex 当前被执行数组元素的索引
- SourceArray,原数组,也就是被调用reduce 方法的数组
如果传入第二个参数,reduce 会在这个参数的基础上开始累计执行
const arr = [1, 2, 3, 4]
const accumulator = (total, current, currentIndex, arr) => {
console.log(total, current, currentIndex, arr);
return total + current
}
console.log(arr.reduce(accumulator))
![9dea822dede8072a251ab7ce9a72e88d.png](https://i-blog.csdnimg.cn/blog_migrate/b9a890632d9ced10a7df823a1c442a93.jpeg)
最终的结果是把所有的数组元素累加起来。值得注意的是,他将数组的第一个元素作为累加的初始值,然后再依次对后面的元素执行reducer 函数。
总共执行了三次,得出最终结果。那如果传入初始值,是怎样的执行顺序?
console.log(arr.reduce(accumulator, 3))
![041190135132a6c68745debadac036f7.png](https://i-blog.csdnimg.cn/blog_migrate/805ce06d9d9186d7ee4a7d19a9cece6b.jpeg)
很明显这次是以传入的初始值作为累加的起点,然后依次对数组执行reducer.
reduce的一些用法:
1.数据扁平化
const array = [[0, 1], [2, 3], [4, 5]]
const flatten = arr => {
return arr.reduce((a, b) => {
return a.concat(b)
}, [])
}
console.log(flatten(array)); // [0, 1, 2, 3, 4, 5]
执行过程
- 第一执行传入初始值[] ,走到reduce的回调函数里参数a就是[] ,参数b就是数组的第一项[0,1]回调函数内就是[].concat([0,1])
- 第二次执行,reduce的回调函数参数a就是上次执行的结果[0,1],本次继续用它concat数组的第二项[2,3],得到结果[0,1,2,3]作为回调函数的参数a继续执行下去
- 依次类推
那么假设数组是这样呢?[[0, [111, 222], 1], [2, [333, [444, 555]], 3], [4, 5]]
,其实加个递归调用就可以
const array = [[0, [111, 222], 1], [2, [333, [444, 555]], 3], [4, 5]]
const flatten = arr => {
return arr.reduce((a, b) => {
if (b instanceof Array) {
return a.concat(flatten(b))
}
return a.concat(b)
}, [])
}
console.log(flatten(array)); // [0, 111, 222, 1, 2, 333, 444, 555, 3, 4, 5]
2.统计字符串中某个字符出现的次数
每次回调函数执行的时候,都会给对象加一个key值,value为出现次数的键值,如果存入字符串value就加1
const str = 'adefrfdnnfhdueassjfkdiskcddfjds'
const arr = str.split('')
const strObj = arr.reduce((all, current) => {
if (current in all) {
// 检测对象里是否包含某个键名的方法一般有三种
all[current]++
} else {
all[current] = 1
}
return all
}, {})
console.log(strObj) // {"a":2,"d":7,"e":2,"f":5,"r":1,"n":2,"h":1,"u":1,"s":4,"j":2,"k":2,"i":1,"c":1}
3.数组去重
const arr = ['1', 'a', 'c', 'd', 'a', 'c', '1']
const afterUnique = arr.reduce((all, current) => {
if (!all.includes(current)) {
all.push(current)
}
return all
}, [])
console.log(afterUnique); // ["1", "a", "c", "d"]
4 按顺序调用promise
这种方式实际上就是处理promise的value,将上一个promise的value 作为下一哥promise的value进行处理
const prom1 = a => {
return new Promise((resolve => {
resolve(a)
}))
}
const prom2 = a => {
return new Promise((resolve => {
resolve(a * 2)
}))
}
const prom3 = a => {
return new Promise((resolve => {
resolve(a * 3)
}))
}
const arr = [prom1, prom2, prom3]
const result = arr.reduce((all, current) => {
return all.then(current)
}, Promise.resolve(10))
result.then(res => {
console.log(res);
})
5 reduce 方法的实现
通过上面的用法,可以总结出来reduce的特点:
- 接收两个参数,第一个为函数,函数内会接收四个参数:Accumulator Current CurrentIndex SourceArray,第二个参数为初始值。
- 返回值为一个所有Accumulator累计执行的结果
Array.prototype.myReduce = function(fn, base) {
if (this.length === 0 && !base) {
throw new Error('Reduce of empty array with no initial value')
}
for (let i = 0; i < this.length; i++) {
if (!base) {
base = fn(this[i], this[i + 1], i, this)
i++
} else {
base = fn(base, this[i], i, this)
}
}
return base
}