最近在国外的网上看到一篇介绍 Optional 中 map 和 flatMap 的文章(需要翻墙),觉得受益良多,给大家分享一下。如果你不知道 Swift 中的 map 和 flatMap,可以看看唐巧的Swift 烧脑体操(四) - map 和 flatMap,这篇文章也有介绍 Optional 中的 map 和 flatMap。
我们先看一个例子
var value: Int? = 2
var newValue = value.map { $0 * 2 }
// newValue is now Optional(4)
value = nil
newValue = value.map { $0 * 2 }
// newValue is now nil
在 Optional 中,map 方法不会关心自己是否是.None,可以直接进行修改,而如果不用 map,那么可能要先保证自己不是.None才能进行修改,比如我们对上面的例子做一个修改
if value != nil {
newValue = value! * 2
}
如果我们要对 value 进行运算,那么首先要保证 value 有值才能进行,相比于用 map 的方式,很明显要臃肿。
map 方法提供了一种无需对 Optional 进行解包就可以直接修改的方式,很便捷。
我们再来看看 flatMap
value = 10
newValue = value.flatMap{ $0 / 5 }
// newValue is now Optional(2)
咋一看,好像没啥区别,那么换一种写法
newValue = value.map({ (i) -> Int in
i / 5
})
newValue = value.flatMap({ (i) -> Int? in
if i < 5 {
return nil
}
return i / 5
})
此时我们可以看到,map 和 flatMap 在闭包中的返回值是不同的,map 的返回值不能是 nil,但是 flatMap 可以。我们在具体使用时,要根据 Closure 中的返回值类型来选择不同的方法。
再来几个例子:
String 字面量值
// Without map:
func ageToString(age: Int?) -> String {
return age == nil ? "Unknown age" : "She is \(age!) years old"
}
// With map:
func ageToString(age: Int?) -> String {
return age.map { "She is \($0) years old" } ?? "Unknown age"
}
本地化 String
// Without map:
let label = UILabel()
func updateLabel(value: String?) {
if let value = value {
label.text = String.localizedStringWithFormat(
NSLocalizedString("value %@", comment: ""), value)
} else {
label.text = nil
}
}
// With map:
let label = UILabel()
func updateLabel(value: String?) {
label.text = value.map {
String.localizedStringWithFormat(NSLocalizedString("value %@", comment: ""), $0)
}
}
根据某一条件在数组中找到对应元素
struct Item {
let identifier: String
}
let items: [Item]
// Without map:
func find(identifier: String) -> Item? {
if let index = items.indexOf({$0.identifier == identifier}) {
return items[index]
}
return nil
}
// With map:
func find(identifier: String) -> Item? {
return items.indexOf({$0.identifier == identifier}).map({items[$0]})
}
解析 Json 字典
struct Person {
let firstName: String
let lastName: String
init?(json: [String: AnyObject]) {
if let firstName = json["firstName"] as? String, let lastName = json["lastName"] as? String {
self.firstName = firstName
self.lastName = lastName
return
}
return nil
}
}
// Without map:
func createPerson(json: [String: AnyObject]) -> Person? {
if let personJson = json["person"] as? [String: AnyObject] {
return Person(json: personJson)
}
return nil
}
// With map:
func createPerson(json: [String: AnyObject]) -> Person? {
return (json["person"] as? [String: AnyObject]).flatMap(Person.init)
}