本文翻译自Swift Guide to Map Filter Reduce
#0
使用map、filter、reduce操作诸如Array或Dictionary的Swift集合可能是之前不常用的。如果你有函数式语言编程经验,你可能知道这与for-in循环类似。这里是我的一些使用指导。
环境为Xcode 9.3 Swift 4.1
Map
map表示循环集合,并对集合中每个元素做相同的操作。map方法返回一个包含对每个元素应用mapping或transform方法的数组:

我们可以用for-in循环来计算数组中的每个元素:
1 | let values = [2.0,4.0,5.0,7.0] |
例子中声明了一个数组squares,在循环中是比较啰嗦的。我们需要一个squares数组在循环中保存结果。如果我们使用map会是这样:
1 | let squares2 = values.map {$0 * $0} |
这是非常大的改善。在map中我们不需要关注循环,map会去做。结果值squares是一个let或者不可变值,我们不需要申明类型,Swift会推断出来。
括号中的闭包起初很难追踪值。在闭包中,map方法提供单参数表示集合中的每个值。闭包处理集合中的每个元素并返回结果,map方法把这些结果返回为一个数组。把map方法写的更详细能够更容易理解,如下:
1 | let squares3 = values.map({ |
这个闭包有个单参数(value: Double)并且返回一个Double,其实Swift可以推断出来。map中有单参数时我们可以省略括号,写在一行时也可以省略return:
1 | let squares4 = values.map {value in value * value} |
关键字in这这里是分隔参数和闭包主体的。如果你想进一步省略,可以用数字参数来缩写:
1 | let squares5 = values.map { $0 * $0 } |
返回值的类型不一定与原集合中的元素类型一致。这里给个map的例子,把integers转为strings:
1 | let scores = [0,28,124] |
map操作不仅可以操作数组,任何集合类型都可以操作。比如,在Dictionary或者Set中应用map,结果依然是返回一个Array。这里是一个Dictionay的例子:
1 | let milesToPoint = ["point1":120.0,"point2":50.0,"point3":70.0] |
快速提示:如果你对理解参数类型有疑惑,Xcode的代码补全可以帮助你
Filter
filter对集合循环操作,返回一个数组,数组仅仅包含匹配条件的元素。

filter方法有个单参数表示条件。闭包把条件应用在集合中的元素上,条件参数的返回值必须是Bool类型。
一个应用filter的例子,筛选数组中的偶数:
1 | let digits = [1,4,10,15] |
Reduce
reduce把集合中的每个元素结合在一起,返回一个新的值。

reduce方法有两个变量,第一个是初值,第二个是闭包。例如,初值为10,把数组的每个值求和:
1 | let items = [2.0,4.0,5.0,7.0] |
这里也可以用 + 操作来缩写:
1 | let codes = ["abc","def","ghi"] |
参数是一个闭包,你可以用尾闭包的形式写reduce:
1 | let names = ["alan","brian","charlie"] |
FlatMap and CompactMap
flatMap和CompactMap是map的变化,对结果的展开或者紧凑。这里给了三种应用情况:
- 在数列上应用
flatMap,闭包中返回一个数列
1 | Sequence.flatMap<S>(_ transform: (Element) -> S) |
这可能是我第一次在Swift中使用flatMap的情景。应用闭包把数列中的每个元素展开的结果:
1 | let results = [[5,2,7], [4,8], [9,1,3]] |
- 在
optional上使用flatMap
闭包处理非nil值,返回一个optional
1 | Optional.flatMap<U>(_ transform: (Wrapped) -> U?) -> U? |
如果原optional是nil,那么flatMap返回nil:
1 | let input: Int? = Int("8") |
- 在数列上使用
compactMap闭包,返回一个optional
1 | Sequence.compactMap<U>(_ transform: (Element) -> U?) -> U? |
注意在Swift 4.1中flatMap的这种使用被重命名为compactMap。这里提供一个简单的方法从数组是剔除nil值:
1 | let keys: [String?] = ["Tom", nil, "Peter", nil, "Harry"] |
详情参考Replacing flatMap with compactMap.
Chaining
这些方法都可以链式使用。比如,对一个数组中所有大于等于7的数字求和:
1 | let marks = [4,5,8,2,9,7] |
另一个例子,从数组中选出偶数值并对选出的值平方
1 | let numbers = [20,17,35,4,12] |
总结
下次如果你需要对集合循环操作时,尝试是否可以使用map、filter、reduce
map返回一个数组,里面每个元素是对原集合每个元素应用统一操作filter返回一个数组,里面的元素是对原集合的元素应用某个条件筛选后的reduce返回一个值,该值是给定一个初识值并对原集合每个元素与初值计算后的结果