Thinking in Swift 系列文章翻译2

这里是原文地址 原文链接

本篇是系列的第二篇文章,是关于数组的映射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
            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] 等。

你可能感兴趣的:(Thinking in Swift 系列文章翻译2)