今天你将学习如何使用最强大的 JavaScript函数。
数组归约
"
Array.reduce
是最强大的JavaScript Function.
时期。 "
reduce有什么作用?为何如此强大?
这是reduce的技术定义......
Array.prototype.reduce()
reduce() 方法对数组的每个元素执行(您提供的)reducer 函数,从而产生单个输出值。
如果你问我的话,这没什么帮助,所以让我们边做边学
1. 求所有数字的总和(使用reduce)
typescript
[3, 2.1, 5, 8].reduce((total, number) => total + number, 0)// loop 1: 0 + 3// loop 2: 3 + 2.1// loop 3: 5.1 + 5// loop 4: 10.1 + 8// returns 18.1
2. 求所有相乘数的总和
typescript
[3, 2.1, 5, 8].reduce((total, number) => total * number, 1)
如图所示,将数组中的所有数字相加或相乘非常容易。但是, -我说过,reduce 是所有 JS 中最强大的函数。
假设我们有3
、4
、10
和60
。假设我们想要获得一些值。
或者,更具体地说,我们想要filter
这些值,并且只返回小于 10 的值。
通常,我们可以简单地使用该filter
函数,只返回小于 10 的数字。
3. 使用reduce进行Array.Filter
ini
[3, 4, 10, 60].filter(number => number < 10)
好吧,使用reduce------我们可以做同样的事情。
typescript
[3, 4, 10, 60].reduce((list, number) =>
number < 10
? [...list, number]
: list, [])
拉拉,我们用 reduce替换了过滤器 - 很酷,但说实话。这仍然不能证明
reduce
它是所有 JavaScript 中最强大的功能。
如果我告诉你,我们可以继续沿着这条路走,并使用 reduce 替换 JavaScript 中的几乎所有数组函数,会怎么样?
4. 重新创建Array.some
使用Array.reduce
sql
[3, 4, 10, 50].some(number => number < 50)// returns true (We have some numbers in the array are less than 50)
使用reduce,我们只需将初始值设置为false。如果条件已经为真,那么我们返回该条件。如果条件尚不成立,那么我们检查当前数字是否满足我们的条件。
typescript
[3, 4, 10, 50].reduce((condition, number) =>
condition === true
? condition
: number < 50, false)
请注意,这次我们以 bool (false) 作为初始值,而不是使用数字或数组。
我们现在已经求和、相乘、过滤并重新创建了一些(也称为使用reduce 有条件地检查数组上的某些内容)。
我们还可以继续
Array.every
使用 using 替换该函数,但由于这与替换usingArray.reduce
类似,因此我们也可以轻松地做到这一点。Array.some``Array.reduce
5.Array.join
4.使用 怎么样Array.reduce
?
替换
Array.join
为Array.reduce
bash
['truck', 'car', 'people'].join('-')// "truck-car-people"
使用
Array.reduce
我们可以编写以下代码
javascript
['truck', 'car', 'people'].reduce((text, word) => `${text}-${word}`, '')// "-truck-car-people"
请注意,输出的前面有一个破折号。
作为函数的第一个参数接受的回调函数
Array.reduce
接受更多参数。我们可以使用第三个接受的参数来跟踪我们index
的reduce函数
javascript
['truck', 'car', 'people'].reduce((text, word, index) =>
index === 0
? word
: `${text}-${word}`, '')// "truck-car-people"
设置第三个参数后,这个reduce函数现在将按照原始
Array.join
函数的方式运行
至此,我们已经使用reduce进行替换了。
- Array.map
- 数组过滤器
- 数组.every,数组.some
- 数组连接
6. 使用Reduce进行Array.concat
那么连接呢?在哪里可以将"1"、"2"和"3"的数组与另一个数组连接起来?
css
[1, 2, 3].concat(['hey', 'world', 'mars'])// [1, 2, 3, 'hey', 'world', 'mars']
您将如何连接或组合数组reduce?
css
[[1,2,3], ['hey', 'world', 'mars']].reduce(
(list, array) => [...list, ...array],[])// [1, 2, 3, 'hey, 'world', 'mars']
使用组合数组的好处
Array.reduce
是我们可以"连接"任意数量的数组。简单地说,通过传入更多数组,我们将使用reduce 自动组合(也称为连接)它们。
这样,我们就复制了
Array.concat
使用Array.reduce
让我们再看几个例子。
首先,让我们创建一些人。
bash
let sarah = { name: 'sarah', email: 'sarah@gmail.com', id: 1 }let tim = { name: 'tim', email: 'tim@gmail.com', id: 2 }let len = { name: 'len', email: 'len@gmail.com', id: 3 }
7. 使用按姓名对人员进行分组Array.reduce
当我们按名字对人进行分组时我们想要的示例
python
people.len// Gets Len// { name: 'len', email: 'len@gmail.com', id: 3 }people.sarah// Gets sarah// { name: 'sarah', email: 'sarah@gmail.com', id: 1}
使用reduce 按姓名对人员进行分组
将reduce函数的初始值设置为一个对象
构建一个对象,其中
- 关键是人的名字([person.name])
- 该值为 person 对象([person.name]: person)
示例(这不起作用)
less
let people = [sarah, tim, len].reduce((people, person) => {
[person.name]: person,
...people}, {})
在上面的例子中我们会得到一个错误
未捕获的语法错误:意外的标记":"
每当我们使用简写函数返回一个对象时,我们需要将其括在括号中
- 将返回对象的方括号括在圆括号中以修复错误
css
let people = [sarah, tim, len].reduce((people, person) => ({
[person.name]: person,
...people}), {})
啦啦,我们现在有一个人员对象,其中人员按姓名分组
如果我们去,people.len
我们会得到 len
python
people.len // { name: 'len', email: 'len@gmail.com', id: 3 }
如果我们去的话people.sarah
我们会得到莎拉
bash
people.sarah // { name: 'sarah', email: 'sarah@gmail.com', id: 1 }
如果我们去的话people.tim
我们会得到蒂姆
bash
people.tim // { name: 'tim', email: 'tim@gmail.com', id: 2 }
如果我们想要我们所有的people
?
css
// people {
sarah: { name: 'sarah', email: 'sarah@gmail.com', id: 1 },
tim: { name: 'tim', email: 'tim@gmail.com', id: 2 },
len: { name: 'len', email: 'len@gmail.com', id: 3 },}
8. 使用Reduce 按给定键提取值数组
更重要的是,如果我们只想获取人员的姓名怎么办?
css
let names = [sarah, tim, len].reduce((names, person) => [ ...names, person.name], [])// ['sarah', 'tim', 'len']
如果我们只想获取人们的电子邮件怎么办?
css
let emails = [sarah, tim, len].reduce((emails, person) => [ ...emails, person.email], [])// ['sarah@gmail.com', 'tim@gmail.com', 'len@gmail.com']
9. 使用Reduce 展平多层嵌套数组
更重要的是,如果我们有一个嵌套数组的数组怎么办?
ini
let list_of_arrays = [
['sub_one', 'sub_two', 'sub_three'],
[
['nested_sub_one', 'nested_sub_two'],
['nested_sub_three', 'nested_sub_four']
],
'one',
'two',
'three']
让我们使用数组列表,当然还可以使用reduce
javascript
list_of_arrays.reduce((flattened, item) => {
if (Array.isArray(item) === false) {
return [...flattened, item]
}
if (Array.isArray(item) && Array.isArray(item[0])) {
return [
...flattened,
....item.reduced((flatten, nested_list) => [...flatten, ...nested_list, [])
]
]
}
return [...flattened, ...item]}, [])
啦啦,我们已经扁平化了多层嵌套数组的列表。
输出
css
["sub_one", "sub_two", "sub_three", "nested_sub_one", "nested_sub_two", "nested_sub_three", "nested_sub_four", "one", "two", "three"]
笔记:
我们只处理最多 3 层深度的嵌套子数组,但是您当然可以在该函数上花费更多时间,并使用递归来非常简单地使用 reduce 来展平无限嵌套层深的数组。
更强大的Reduce用例
好吧,现在让我们深入研究一些更强大但不常用的用例
Array.reduce
。
10. 在字符串上应用格式化程序
我将从一系列字符串开始。
ini
let strings = ['cool-link', 'hello world of javascript', 'goodbye, its been swell']
接下来让我们创建一个formatters
. 通常,我将这些称为过滤器 - 但它们并不是真正的过滤器。他们只是格式化字符串。
这些格式化程序实际上是回调函数。
首先,我们将创建一个破折号到空格格式化程序(用空格替换破折号)。将使用正则表达式来实现此格式化程序。
javascript
let dashesToSpaces = str => str.replace(/-/g, ' ')
接下来,我们将创建一个大写字符串格式化程序。
ini
let capitalize = str => `${str[0].toUpperCase()}${str.slice(1)}`
然后,我们将创建一个字符串限制器格式化程序。
如果字符串大于给定长度,则用三个点替换该长度限制之后的字符。
python
let limiter = str => str.length > 10 ? `${str.slice(0, 10)}...` : str
formatters
最后,我们将使用所有字符串格式化程序创建一个数组。
ini
let formatters = [dashesToSpaces, capitalize, limiter]
请记住,我们有字符串数组。
ini
let strings = ['cool-link', 'hello world of javascript', 'goodbye, its been swell']
我们的目标:
我们的目标是将格式化程序数组中的每个格式化程序应用到字符串数组中的每个字符串上。
使用reduce,我们可以简单地这样做!
typescript
strings.reduce((list, str) => [
formatters.reduce((string, format) => format(string), str),
...list
],[])
就像这样,我们使用reduce 在字符串数组上应用格式化程序数组。
原始字符串数组
css
['cool-link', 'hello world of javascript', 'goodbye, its been swell']
输出(使用reduce应用字符串格式化程序后)
css
["Goodbye, i...", "Hello worl...", "Cool link"]
11.按房间对学生进行分组(使用reduce)
首先我们创建一些学生
ini
let students = [
{ name: 'Sally', room: 'A' },
{ name: 'tim', room: 'A' },
{ name: 'nick', room: 'B' },
{ name: 'rick', room: 'C' },
{ name: 'sarah', room: 'B' },
{ name: 'pam', room: 'C' }]
我们想按学生的房间对他们进行分组
所以我们要做的就是使用students.reduce
.
javascript
students.reduce((class_rooms, student) => ({}), {})
请注意,我们在再次隐式返回的对象周围使用了括号。当我们使用简写函数返回一个对象时,我们必须使用({})
语法 - 如果我们尝试直接返回一个对象而不进行包装,()
我们将收到错误。
接下来,我们要使用学生房间作为钥匙:
ini
students.reduce((rooms, student) => ({
...rooms,
[student.room]: rooms[student.room]
? [...rooms[student.room], student]
: [student]}), {})
现在,我们的学生按房间/班级分组。
css
{
A: [{ name: 'sally', room: 'A' }, { name: 'tim', room: 'A' }],
B: [{ name: 'nick', room: 'B' }, { name: 'sarah', room: 'B'}],
C: [{ name: 'rick', room: 'C' }, { name: 'pam', room: 'C' }],}
我们已经成功地按学生的房间对学生进行了分组 - 这就是我们按归约进行分组的方式。
这就是我对reduce 的了解。我想最大的收获是,reduce 是一种超级方法 - 确实是如此!
使用reduce 可以做任何其他数组方法可以做的事情。
Array.filter.map.filter.forEach
您可以使用单个reduce 函数来完成相同的目标。
如果您需要按键对一大堆对象进行分组,请使用reduce。
如果您需要提取与给定键相关的值?使用reduce。
如果您需要应用多个过滤器,但又不想通过多次迭代同一数组来提高时间复杂度 - 请使用reduce。
如果您想展平嵌套数组的数组,其中每个嵌套数组可能有更多嵌套数组,而每个嵌套数组也可能没有任何嵌套数组?使用reduce。
如果你需要对一些数字进行求和、乘以一些数字、减去总和或进行任何类型的算术------reduce 会再次起作用。
如果需要组合一些数组怎么办?使用reduce。
如果您需要组合一些对象怎么办?使用reduce。
如果您想拥有一种方法,您知道它可以完成所有工作,并且让您感觉作为软件工程师更加强大和高效,该怎么办?使用reduce!
在我看来,forEach 是 JavaScript 生态系统中最被高估的方法,reduce 是JS 生态系统中最被低估的方法。
作为最后一个例子,让我们以这个最后的例子来说明reduce 有多牛。
scss
[{ name: 'Clean Code Studio' }, { belief: 'Simplify!' }, { should_follow: 'Si, senor!' }].reduce((last_example, partial) => ({
...last_example, ...partial }), {})
这会返回什么?它合并所有对象。
css
{
name: 'Clean Code Studio',
belief: 'Simplify',
should_follow: 'Si, senor!'}
使用reduce,你可以过滤,你可以应用,你可以应用回调列表,你可以展平,合并,组合......
我强烈建议您熟悉、胜任并且总体上熟悉如何使用reduce。
同样,使用reduce你有两个参数。
- 累加器 - 回调函数
- 初始值 - 在累加器回调函数第一次迭代期间使用
scss
[].reduce(accumulatorCallbackFunction, initialValue)
Accumulator回调函数有四个参数
- accumulator - 每次迭代后从回调函数返回的值
- item - 数组中的元素
- index - 传递到累加器回调中的当前元素的索引
- source - 正在调用原始数组reduce
scss
let initial = []let callback = (accumulator, item, index, source) => {}[].reduce(callback, initial)
最后,最后一个额外的提示 - 如果您想在迭代完所有项目之前突破归约,该怎么办?
scss
[].reduce((build, item, index, source) => source.slice(index), 0)
通过在给定索引处对源数组进行切片,您将摆脱reduce函数循环 - 因此,如果您有一个大数据集,一旦满足条件,您就可以停止使用计算资源。
最后,我强烈建议练习reduce。我发现它绝对是最有用的 JavaScript 函数。很多时候,reduce 已经成为以简洁、中肯的方式解决复杂编码挑战的解决方案。