lro gro
Swift borrows many functional programming concepts, among others: map
, flatMap
and compactMap
. This article will give a closer look at these operations.
Swift借鉴了许多函数式编程概念,其中包括map
, flatMap
和compactMap
。 本文将详细介绍这些操作。
地图 (map)
Suppose that we have detectives with: name, rank and cases working on. To find out which detectives are heavily involved in solving cases we can implement the following approach
假设我们有侦探,他们的名字,职级和案件正在研究中。 为了找出哪些侦探在解决案件中投入大量精力,我们可以采用以下方法
struct Detective {
let name: String
let rank: String
let cases: UInt
}func involvedInCases(detectives: [Detective]) -> [String] {
var involvments = [String]()
for detective in detectives {
let involvment: String;
switch detective.cases {
case 0: involvment = "\(detective.name) is not involved"
case 1...10: involvment = "\(detective.name) is pretty involved"
default: involvment = "\(detective.name) is heavily involved"
}
involvments.append(involvment)
}
return involvments
}
This is a good start, but it can be improved with map
operation. With map
you can transform each value of the array into something that is desirable. map
receives a closure which gets called for each element of the array. After it finishes it returns a new mapped array.
这是一个好的开始,但是可以通过map
操作进行改进。 使用map
可以将数组的每个值转换为所需的值。 map
收到一个闭包,该闭包被数组的每个元素调用。 完成后将返回一个新的映射数组。
func involvedInCases(detectives: [Detective]) -> [String] {
return detectives
.map { detective -> String in
switch detective.cases {
case 0: return "\(detective.name) is not involved"
case 1...10: return "\(detective.name) is pretty involved"
default: return "\(detective.name) is heavily involved"
}
}
}
This looks pretty good, and can be extended even more. The result of map
can be put into pipeline to perform additional transformations. Imagine that we need to get the names of detectives who are not involved in any case in order to assign them some. We can accomplish this by creating a following construct:
这看起来不错,并且可以扩展得更多。 可以将map
的结果放入管道中以执行其他转换。 想象一下,我们需要获取在任何情况下都没有参与的侦探的姓名,以便为其分配一些侦探的姓名。 我们可以通过创建以下结构来实现:
func getUninvovedDetectivesSorted(detectives: [Detective]) -> [String] {
return detectives
.filter { $0.cases == 0 }
.map { $0.name }
.sorted(by: <)
}
A pipeline defines a clear and immutable way of transforming data into separate steps. Generally, a pipeline is performant enough and worth the expressive and immutable nature.
管道定义了将数据转换为单独步骤的清晰且不变的方式。 通常,管道具有足够的性能,值得具有表达性和不变性。
An Array
is not the only collection type which you can use map
over. You can use any Collection
and Sequence
protocol implementation as well as Optional
s.
Array
不是唯一可以使用map
集合类型。 您可以使用任何Collection
和Sequence
协议实现以及Optional
。
flatMap (flatMap)
The flatMap
function is similar to map
except that it “flattens” the resulting array. With map
you can end up with nested type, flatMap
is there to handle this case. The example below shows detective's scores on each test, and we want to check if the average result is higher than some threshold value:
flatMap
函数与map
相似, flatMap
在于它“展平”了结果数组。 使用map
您可以使用嵌套类型来结束, flatMap
可以处理这种情况。 下面的示例显示了每次测试中侦探的得分,我们要检查平均结果是否高于某个阈值:
let detectiveScoresByName = [
"John": [100, 35, 86],
"Nick": [22, 56, 86],
"Ron": [92, 1, 34]]
let mapped = detectiveScoresByName.map { $0.value }
// [[100, 35, 86], [22, 56, 86], [92, 1, 34]] - An array of arrays
let flatMapped = detectiveScoresByName.flatMap { $0.value }
// [100, 35, 86, 22, 56, 86, 92, 1, 34] - flattened to only one array// implementation of the check omitted
So map
transforms an array of values into an array of other values, and flatMap
does the same thing, but also flattens the result of nested collections into just a single array.
因此, map
将值的数组转换为其他值的数组, flatMap
做相同的事情,但还将嵌套集合的结果展平为单个数组。
compactMap (compactMap)
Finally, there’s compactMap
, which lets us discard any nil
values that our transform might produce. Instead of flattening nested collection, this function performs flattening optionals inside the collection.
最后,还有compactMap
,它使我们可以丢弃转换可能产生的任何nil
值。 代替展平嵌套的集合,此函数在展馆内部执行展平可选项。
Let's go over an example that will shine a light on this type of operation. A user is providing a list of his/hers favorite websites, which we are trying to reach from code. We will try to turn passed strings into URLs and then perform operation:
让我们来看一个示例,该示例将阐明这种类型的操作。 用户正在提供他/她最喜欢的网站的列表,我们正在尝试从代码中访问这些列表。 我们将尝试将传递的字符串转换为URL,然后执行操作:
let urls = [
"http://www.google.com",
"http://www.duckduckgo.com",
"http://www.bing.com",
"http://my search engine.com"]
let mapped = urls.map { URL(string: $0) }
// [Optional(http://www.google.com), Optional(http://www.duckduckgo.com), Optional(http://www.bing.com), nil]
let compactMapped = urls.compactMap(URL.init)
// [http://www.google.com, http://www.duckduckgo.com, http://www.bing.com]// implementation of the ping omitted
As construction of URL
can fail and return nil
, map
would not produce correct output (it will contain nil
). Additionally, map
would produce elements wrapped in Optional
which should be handled. compactMap
takes care of both problems for us.
由于URL
构建可能会失败并返回nil
,因此map
不会产生正确的输出(它将包含nil
)。 此外, map
会产生包装在Optional
中的元素,这些元素应进行处理。 compactMap
为我们解决了这两个问题。
As for map
, both flatMap
and compactMap
can be chained (used in pipeline).
至于map
, flatMap
和compactMap
都可以链接(在管道中使用)。
重要要点 (Key takeaways)
map
transforms an array of values into an array of other valuesmap
将值数组转换为其他值数组flatMap
does the same thing, but also flattens the result of nested collections into just a single arrayflatMap
做同样的事情,但是也将嵌套集合的结果展平为单个数组when an
Optional
isnil
,map
andflatMap
will ignore any chained operations当
Optional
为nil
,map
和flatMap
将忽略任何链接的操作with
compactMap
you can filternil
values out of arrays and sequences ofOptional
s使用
compactMap
您可以从Optional
的数组和序列中过滤出nil
值- everything can be done in imperative way as well 一切也可以以命令方式完成
- if you are considering performance as a key factor, you should test both imperative and functional way in order to tailor the code to your needs 如果您将性能视为关键因素,则应该同时测试命令式和功能性方式,以便根据需要定制代码
翻译自: https://medium.com/swlh/swift-grokking-map-flatmap-and-compactmap-a675f9c2e4ff
lro gro