这里是原文地址 原文链接
本篇是系列的第二篇文章,是关于数组的映射map()和flatMap()的。
1. 上篇结束时的代码如下
class ListItem {
var icon: UIImage?
var title: String = ""
var url: NSURL!
static func listItemsFromJSONData(jsonData: NSData?) -> [ListItem] {
guard let nonNilJsonData = jsonData,
let json = try? NSJSONSerialization.JSONObjectWithData(nonNilJsonData, options: []),
let jsonItems = json as? Array<NSDictionary>
else {
// If we failed to unserialize the JSON or that JSON wasn't an NSArray,
// then bail early with an empty array
return []
}
var items = [ListItem]()
for itemDesc in jsonItems {
let item = ListItem()
if let icon = itemDesc["icon"] as? String {
item.icon = UIImage(named: icon)
}
if let title = itemDesc["title"] as? String {
item.title = title
}
if let urlString = itemDesc["url"] as? String, let url = NSURL(string: urlString) {
item.url = url
}
items.append(item)
}
return items
}
}
目的是让上边的代码更像swift的代码。
2. 介绍map()
map()是一个方法,通过传入一个函数 作为参数,可以将array中的每个item转换为新的item。 既通过解释如何将x->y 可以将array[x] 转换为array[y],不用通过创建一个临时变量 mutable array。
所以本例中,通过map,不用使用for循环,和创建临时可变数组变量。如下
return jsonItems.map { (itemDesc: NSDictionary) -> ListItem in
let item = ListItem()
if let icon = itemDesc["icon"] as? String {
item.icon = UIImage(named: icon)
}
if let title = itemDesc["title"] as? String {
item.title = title
}
if let urlString = itemDesc["url"] as? String, let url = NSURL(string: urlString) {
item.url = url
}
return item
}
3,数据损坏
使用map来修改代码后, 有一个问题我们还没有解决:我们依然创建一个ListItem即使我们的原始数据不正确。
如果代表着一个对象的字典数据不正确,我们会创建一个空的ListItem,没有意义。
更重要的一点,因为我们对象的url属性是 NSURL!的,我们的代码依然允许我们创建一个ListItem对象,即使url属性是空的,那么访问该对象的时候,就会carsh
为了解决这个问题,我们可以返回一个nil 如果输入数据是不合法的。看起来比空的ListItem对象更合适
return jsonItems.map { (itemDesc: NSDictionary) -> ListItem? in
guard …/* condition for valid data */… else { return nil }
let realValidItem = ListItem()
… /* fill the ListItem with the values */
return realValidItem
}
// 但是这样,一旦数据不合法,会返回nil,产生带有 nil 的 [ListItem?] 数组。依然不太合适。
4. 使用 flatMap()
flatMap和map类似,但是 它是用 T->U? 代替 T->U ,而且,不会添加nil 到数组中。
使用方法和map类似
return jsonItems.flatMap { (itemDesc: NSDictionary) -> ListItem? in
guard let title = itemDesc["title"] as? String,
let urlString = itemDesc["url"] as? String,
let url = NSURL(string: urlString)
else { return nil }
let li = ListItem()
if let icon = itemDesc["icon"] as? String {
li.icon = UIImage(named: icon)
}
li.title = title
li.url = url
return li
}
// 现在,只返回 有title,有url的item。
5. 结论
使用map和flatmap 可以代替 for循环。
使用flatmap还可以在转换的时候过滤不合法的数据源。
// note
flatMap还有别的用法,比如 [[T]] -> [T] 等。