Appearance
12. 万物皆可 reduce
要是有人问我 JS 中最喜欢的一个内置方法,那我的回答必然是是 Array.prototype.reduce
。
遇事不决,reduce 一把梭(开玩笑hh,还是看应用场景),不过本文讲的是 reduce 的各种用法。理论上,reduce 也可以代替很多其他常用方法,包括 map、filter 等等。
关于 reduce 的语法,请移步 MDN reduce。
1. 求和
js
/**
* @param {...number} args
* @returns {number}
*/
const sum = (...args) =>
args.reduce((preSum, curNum) => preSum + curNum, 0)
sum(1, 2, 3) // 6
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
2. 计算字符出现的次数
js
/**
* @param {string} s
* @param {string} c
* @returns {number}
*/
const count = (s, c) =>
s.split('').reduce(
(total, curChar) => total + (curChar === c ? 1 : 0),
0
)
count('ababbab', 'a') // 3
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
3. 统计字符
js
/**
* @param {string} str
* @returns {number}
*/
const countOf = str =>
str.split('').reduce((preObj, curChar) => {
preObj[curChar] = (preObj[curChar] ?? 0) + 1
return preObj
}, {})
countOf('abbcccdddde') // {a: 1, b: 2, c: 3, d: 4, e: 1}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
4. 求最值
求最大值
js
/**
* @param {...number} nums
* @returns {number}
*/
const max = (...nums) =>
nums.reduce(
(preMax, curNum) => (preMax < curNum ? curNum : preMax),
-Infinity
)
max(1, 5, 3, 2, 6, 4) // 6
max() // -Infinity
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
求最小值
js
/**
*
* @param {...number} nums
* @returns {number}
*/
const min = (...nums) =>
nums.reduce(
(preMin, curNum) => (preMin > curNum ? curNum : preMin),
Infinity
)
min(1, 5, 3, 2, 6, 4) // 1
min() // Infinity
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
5. 数组扁平化
js
/**
* @param {any[]} arr - 待拍平的数组
* @param {number} depth - 要拍平的深度,默认为 1 层
* @returns {any[]}
*/
const flatArr = (arr, depth = 1) => {
return arr.reduce((pre, cur) => {
if (Array.isArray(cur) && depth > 1) {
return [...pre, ...flatArr(cur, depth - 1)]
} else {
return [...pre, cur]
}
}, [])
}
const arr = [1, [2, [3, 4, [5, 6], 7]]]
flatArr(arr) // [ 1, 2, [ 3, 4, [ 5, 6 ], 7 ] ]
flatArr(arr, 1) // [ 1, 2, [ 3, 4, [ 5, 6 ], 7 ] ]
flatArr(arr, 2) // [ 1, 2, 3, 4, [ 5, 6 ], 7 ]
flatArr(arr, Infinity) // [ 1, 2, 3, 4, 5, 6, 7 ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
6. 数组去重
js
/**
* @param {number[]} nums
* @returns {number[]}
*/
const deDup = nums => {
return nums.reduce(
(preArr, curNum) =>
preArr.concat(preArr.includes(curNum) ? [] : [curNum]),
[]
)
}
deDup([1, 2, 2, 3, 3, 3, 4, 5, 5]) //[ 1, 2, 3, 4, 5 ]
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
7. 插入排序
js
/**
* @param {number[]} nums
* @returns {number}
*/
const insertSort = nums => {
return nums.reduce((pre, cur) => {
let i = 0
for (; i < pre.length; i++) {
if (pre[i] > cur) break
}
return [...pre.slice(0, i), cur, ...pre.slice(i)]
// return pre.slice(0, i).concat(cur).concat(pre.slice(i))
}, [])
}
insertSort([4, -3, 11, 5, 7, 4n]) // [-3, 4, 4, 5, 7, 11]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
8. 合并对象数组
js
/**
* @param {object[]} objArr
* @returns {object}
*/
const merge = objArr =>
objArr.reduce((pre, { key, value }) => {
pre[key] = value
return pre
}, {})
const obj = [
{ key: 'name', value: 'tom' },
{ key: 'age', value: 20 },
{ key: 'sex', value: 'male' }
]
merge(obj) // { name: 'tom', age: 20, sex: 'male' }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
9. 实现 map 方法
reduce
能做的事情很多,甚至完全能够代替 map 方法。先来看一个例子:
js
/**
* @param {number[]} nums
* @returns {number[]}
*/
const squareOf = nums =>
nums.reduce(
(pre, cur) => [...pre, cur ** 2], []
)
squareOf([1, 2, 3]) // [1, 4, 9]
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
上面这个例子等价于 arr.map(x => x ** 2)
。
下面是 reduce 方法实现的 map 方法,更加说明 reduce 可以完全代替 map:
js
/**
* @param {(value: any, index: number, array: any[]) => any} callback
* @param {any} thisArg
* @returns {any[]}
*/
Array.prototype.reduceMap = function (callback, thisArg) {
return this.reduce((pre, cur, index, arr) => {
cur = callback.call(thisArg, cur, index, arr)
pre.push(cur)
return pre
}, [])
}
let a = [1, 2, 3].reduceMap(function (x) {
return x ** 2 + this.a
}, { a: 1 })
console.log(a) // [2, 5, 10],先平方,后+1
const double = [1, 2, 3].reduceMap(x => x * 2) // [2, 4, 6]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
10. 实现 filter 方法
js
/**
* @param {(value: any, index: number, array: any[]) => any} callback
* @param {any} thisArg
* @returns {any[]}
*/
Array.prototype.reduceFilter = function (callback, thisArg) {
return this.reduce(
(pre, cur, index, arr) => {
return pre.concat(
callback.call(thisArg, cur, index, arr) ? cur : []
)
},
[]
)
}
;[1, 2, 3, 4, 5, 6].reduceFilter(x => x & 1)
// [1, 3, 5],过滤出奇数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
总结
本文是总结 reduce 一些常用使用场景,旨在说明了这个方法的强大之处。但不代表每个场景中都要使用,比如该用 map、filter 的还是得用,怎么优雅怎么来。
有一个实验性的 Array.prototype.group()
,浏览器暂时还不支持,但我们也可以使用 reduce
实现,这个就交给你了。