图解 Map、Reduce 和 Filter 数组方法

2,020 阅读4分钟

Array.map()

Array.map() 根据传递的转换函数,更新给定数组中的每个值,并返回一个相同长度的新数组。它接受一个回调函数作为参数,用以执行转换过程。

let newArray =oldArray.map((value,index,array)=>{
  ...
});

一个帮助记住 map 的方法:Morph Array Piece-by-Piece(逐个改变数组) 你可以使用 map 代替 for-each 循环,来遍历并对每个值应用转换函数。这个方法适用于当你想更新数组的同时保留原始值。它不会潜在地删除任何值(filter 方法会),也不会计算出一个新的输出(就像 reduce 那样)。map 允许你逐个改变数组。一起来看一个例子:

[1,4,6,14,32,78].map(val =>val *10)
// the result is: [10, 40, 60, 140, 320, 780]

mmbiz.qpic.cn/mmbiz_jpg/m…

Array.filter()

当我们想要过滤数组的值到另一个数组,新数组中的每个值都通过一个特定检查,Array.filter() 这个快捷实用的方法就派上用场了。 类似搜索过滤器,filter 基于传递的参数来过滤出值。

举个例子,假定有个数字数组,想要过滤出大于 10 的值,可以这样写:

[1,4,6,14,32,78].filter(val =>val >10)
// the result is: [14, 32, 78]

但是 filter 方法,只返回真值。因此如果所有值都执行指定的检查的话,结果的长度会小于等于原始数组。

把 filter 想象成一个漏斗。部分混合物会从中穿过进入结果,而另一部分则会被留下并抛弃。

mmbiz.qpic.cn/mmbiz_jpg/m…

假设宠物训练学校有一个四只狗的小班,学校里的所有狗都会经过各种挑战,然后参加一个分级期末考试。我们用一个对象数组来表示这些狗狗:

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
})

Array.reduce()

reduce() 方法接受一个数组作为输入值并返回一个值。这点挺有趣的。reduce 接受一个回调函数,回调函数参数包括一个累计器(数组每一段的累加值,它会像雪球一样增长),当前值,和索引。reduce 也接受一个初始值作为第二个参数:

let finalVal = oldArray.reduce((accumulator,currentValue,currentIndx
,array)=>{
...
}),initalValue;

mmbiz.qpic.cn/mmbiz_jpg/m…

来写一个炒菜函数和一个作料清单:

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"

Array​.prototype​.flatMap()

flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。

语法

var new_array = arr.flatMap(function callback(currentValue[, index[, array]]) {
    // 返回新数组的元素
}[, thisArg])

参数

callback 可以生成一个新数组中的元素的函数,可以传入三个参数:

currentValue 当前正在数组中处理的元素 index可选 可选的。数组中正在处理的当前元素的索引。 array可选 可选的。被调用的 map 数组 thisArg可选 可选的。执行 callback 函数时 使用的this 值。

返回值

一个新的数组,其中每个元素都是回调函数的结果,并且结构深度 depth 值为1。

示例

Map 与 flatMap

var arr1 = [1, 2, 3, 4];

arr1.map(x => [x * 2]); 
// [[2], [4], [6], [8]]

arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]

// 只会将 flatMap 中的函数返回的数组 “压平” 一层
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]
let arr = ["今天天气不错", "", "早上好"]

arr.map(s => s.split(""))
// [["今", "天", "天", "气", "不", "错"],[""],["早", "上", "好"]]

arr.flatMap(s => s.split(''));
// ["今", "天", "天", "气", "不", "错", "", "早", "上", "好"]

等价操作

归纳(reduce) 与 合并(concat)

var arr1 = [1, 2, 3, 4];

arr1.flatMap(x => [x * 2]);
// 等价于
arr1.reduce((acc, x) => acc.concat([x * 2]), []);
// [2, 4, 6, 8]