映射、缩减和过滤是 JavaScript 中三种非常有用的数组方法,它们为开发人员提供了极大的力量,只需很少的代码量。让我们直接进入如何利用(并记住如何使用!)这些超级方便的方法。
Array.map()
Array.map()
根据提供的转换更新给定数组中的每个单独的值,并返回一个大小相同的新的数组。它接受一个回调函数作为参数,用于应用转换。
let newArray = oldArray.map((value, index, array) => {
...
});
记住这一点的助记符是 **MAP:逐个变形数组**。
您可以使用映射来代替 for-each 循环来遍历并对每个值应用此转换。当您想要保留每个值但更新它时,这很有用。我们不会潜在地消除任何值(就像我们使用过滤器那样),或者计算一个新的输出(就像我们使用缩减器那样)。映射允许您逐个变形数组。让我们来看一个例子。
[1, 4, 6, 14, 32, 78].map(val => val * 10)
// the result is: [10, 40, 60, 140, 320, 780]
在上面的例子中,我们取一个初始数组([1, 4, 6, 14, 32, 78]
),并将其中的每个值映射为该值乘以十 (val * 10
)。结果是一个新数组,其中原始数组的每个值都通过以下等式进行了转换:[10, 40, 60, 140, 320, 780]
。

Array.filter()
Array.filter()
当我们有一个值数组,并且想要将这些值过滤到另一个数组中时,这是一个非常方便的快捷方式,其中新数组中的每个值都是通过特定测试的值。
这就像一个搜索过滤器。我们正在过滤掉通过我们提供的参数的值。
例如,如果我们有一个数值数组,并且想要将其过滤为仅大于 10 的值,我们可以编写以下代码
[1, 4, 6, 14, 32, 78].filter(val => val > 10)
// the result is: [14, 32, 78]
如果我们要对这个数组使用映射方法,例如上面的例子,我们将返回一个与原始数组长度相同的数组,其中val > 10
是“转换”,或者在这种情况下是测试。我们将原始数组中的每个值转换为它们的答案,如果它们大于 10。它看起来像这样
[1, 4, 6, 14, 32, 78].map(val => val > 10)
// the result is: [false, false, false, true, true, true]
但是,过滤器仅返回真实值。因此结果小于原始数组或与原始数组相同,如果所有值都通过特定测试。
将过滤器想象成一个过滤器的类型。一些混合物会通过并进入结果,但一些会被留下并丢弃。

假设我们有一个(非常小)的四个狗的服从学校班级。所有狗在服从学校期间都遇到了挑战,并参加了评分的期末考试。我们将把小狗表示为一个对象数组,即
const students = [
{
name: "Boops",
finalGrade: 80
},
{
name: "Kitten",
finalGrade: 45
},
{
name: "Taco",
finalGrade: 100
},
{
name: "Lucy",
finalGrade: 60
}
]
如果狗在期末考试中获得的成绩高于 70 分,它们将获得一张精美的证书;如果它们没有,它们需要再次参加课程。为了知道要打印多少证书,我们需要编写一个方法来返回通过考试的狗。与其编写一个循环来测试数组中的每个对象,我们可以使用filter
缩短我们的代码!
const passingDogs = students.filter((student) => {
return student.finalGrade >= 70
})
/*
passingDogs = [
{
name: "Boops",
finalGrade: 80
},
{
name: "Taco",
finalGrade: 100
}
]
*/
如您所见,Boops 和 Taco 是好狗(实际上,所有狗都是好狗),因此 Boops 和 Taco 将获得通过课程的成就证书!我们可以使用我们可爱的隐式返回在一行代码中编写此代码,然后从我们的箭头函数中删除括号,因为我们只有一个参数
const passingDogs = students.filter(student => student.finalGrade >= 70)
/*
passingDogs = [
{
name: "Boops",
finalGrade: 80
},
{
name: "Taco",
finalGrade: 100
}
]
*/
Array.reduce()
reduce()
方法接受数组的输入值并返回单个值。这个真的很有趣。Reduce 接受一个回调函数,该函数由一个累加器组成(一个累加数组每个部分的值,像雪球一样增长),值本身以及索引。它还接受一个起始值作为第二个参数
let finalVal = oldArray.reduce((accumulator, currentValue, currentIndex, array) => {
...
}), initalValue;

让我们建立一个烹饪函数和一个配料列表
// our list of ingredients in an array
const ingredients = ['wine', 'tomato', 'onion', 'mushroom']
// a cooking function
const cook = (ingredient) => {
return `cooked ${ingredient}`
}
如果我们想将这些项目缩减为酱汁(双关语绝对是故意的),我们将使用reduce()
将其缩减!
const wineReduction = ingredients.reduce((sauce, item) => {
return sauce += cook(item) + ', '
}, '')
// wineReduction = "cooked wine, cooked tomato, cooked onion, cooked mushroom, "
该初始值(在我们的例子中为''
)很重要,因为如果没有它,我们不会烹饪第一项。它使我们的输出有点奇怪,因此它绝对是需要注意的事情。这就是我的意思
const wineReduction = ingredients.reduce((sauce, item) => {
return sauce += cook(item) + ', '
})
// wineReduction = "winecooked tomato, cooked onion, cooked mushroom, "
最后,为了确保我们的新字符串末尾没有任何多余的空格,我们可以传递索引和数组来应用我们的转换
const wineReduction = ingredients.reduce((sauce, item, index, array) => {
sauce += cook(item)
if (index < array.length - 1) {
sauce += ', '
}
return sauce
}, '')
// wineReduction = "cooked wine, cooked tomato, cooked onion, cooked mushroom"
现在我们可以使用三元运算符、字符串模板和隐式返回更简洁地编写它(在一行中!)
const wineReduction = ingredients.reduce((sauce, item, index, array) => {
return (index < array.length - 1) ? sauce += `${cook(item)}, ` : sauce += `${cook(item)}`
}, '')
// wineReduction = "cooked wine, cooked tomato, cooked onion, cooked mushroom"
记住这一点的一个小方法是回想一下你如何制作酱汁:你缩减几种配料到一个单一项目。
跟我一起唱!
我想用一首歌来结束这篇文章,所以我写了一首关于数组方法的小曲,这可能有助于你记住它们
当然,我留下了一条代码无法正常运行的评论(后座编码!)。应该改为…
const wineReduction = ingredients.reduce((sauce, item) => ([sauce, cook(item)].filter(x => x).join(', ')), '');
这太棒了!感谢你成为如此有创造力的人!:D
很酷的解释!
不过,我想指出缩减中一个潜在的陷阱。在 lambda 中,你写道
return sauce += cook(item) + ‘, ‘
但是,这里不需要使用 +=,因为对 lambda 的下一个调用不会使用相同的 sauce,而是会使用返回值作为它的 sauce。
使用 + 就足够了。+= 也不太符合映射/过滤/缩减的功能风格,因为它会改变 sauce(即使它只是一个局部变量)。
对于我们有逗号检查的第二个版本,我们可以将临时新酱汁保存在一个新变量中,例如
const nextSauce = sauce + cook(item);
我喜欢这篇文章和这首歌。我将现在使用这三者。
这太棒了!
我可以在第一个
reduce
示例中贡献一个小的修复。第二个参数在括号之外,它应该是
解释得很好,我也很喜欢这首歌:) 谢谢
太棒了!谢谢
现在我清楚地理解了 Array.map() 和 Array.filter() 方法,我会再次仔细阅读 .reduce() 方法
非常好的文章。这三个函数(方法)非常有用,如果更多人知道它们会很棒。
不过,我建议;你可以将第一个缩减示例简化为以下内容
ingredients.reduce((sauce, ingredient) => sauce + ‘, ‘ + ingredient)
它不会在第一个参数之前添加逗号,因为它根本不会对第一个参数调用该函数,而是从第二个参数开始,并将第一个参数作为累加器传递。
是的,我也这么想过!;)
文章和歌曲很棒!但我觉得 reduce 的例子太复杂了,使用 `join(", ")` 可以更轻松地获得以逗号分隔的熟食。
很棒的文章,但我认为你忽略了 `reduce()` 的一个重要细节:如果没有提供 `initialValue` 参数,它将默认使用源数组中的第一个元素。你已经在输出注释中展示了这一点,但没有明确解释它(你只是说它会让输出“不正常”。
解释得很好,谢谢。我在 reduce 部分迷路了,但 .map 和 .filter 比以前解释的要简单得多。
演示很不错,但有几点。
1) 你对 map 的措辞让人觉得初始数组会被修改,但事实并非如此。
2) 你对 reduce 的初始代码段是错误的。初始值应该放在括号里。
let finalVal = oldArray.reduce((accumulator, currentValue, currentIndex, array) => {
…
}, initialValue);
Aron 已经提到了其他一些小问题。
另外需要注意的是,`return` 是可选的。
const cook = (ingredient) => {return
cooked ${ingredient}
};可以写成
const cook = (ingredient) =>
cooked ${ingredient}
;ROFL…
你疯了!用积极的方式说 :-)