在本系列文章的第一篇中,我们知道了怎样避免对可选类型强制拆包,尽量少使用 ! 也避免了程序崩溃。在第二部分,我将会精简我们的代码来让它看起来更 “Swift”,同时向你介绍 map()flatMap() 方法。

今天这篇文章我们将要讨论数组的 map()flatMap() 方法。

本系列文章的第一篇里

这是我们上次留下的代码。

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” 的模式和语法来让我们的代码变得更简洁。

map()函数的介绍

map()Array 提供的方法,它接收一个函数作为参数,旧数组中的每个元素都会被拿去执行这个函数而变成新数组的对应元素,这是一种让数组从 [X] 转化为 [Y] 的方式,你需要提供的就是 X -> Y 的转化方式,而不必新建一个临时数组。

在我们例子里,我们不再像之前一样用 for 来做循环,而是对 jsonItems – 我们 NSDictionary 类型的 JSON 数组,使用 map() 方法,为它提供一个参数函数,将原来的每个 NSDictionary 类型转换成我们所需的 ListItem 实例:

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
}

这看起来只是个很小的改变,但是它让我们可以专注于怎样把 NSDictionary 转化成 ListItem ,毕竟这也是我们要解决的核心问题,更重要的是,我们避免了像我们在Objc里做的那样,新建一个中间数组。我们应该尽可能的避免这种情况发生。

错误数据

我们还有个问题要解决,就是,即便输入的数据是不可用的,我们还是创建了一个 ListItem 实例。这样的话,如果我们的某些 NSDictionary 是不可用的,最后的输出数组里,就会有很多无意义的无内容的 ListItem() 实例,其中也就不包含可用的 NSURL

更糟的是,代码允许我们创建那些没有可用 NSURLListItem() 实例,而我们在 NSURL! 中使用了 !,这样一旦执行了 NSURL! 这样的强制拆包,我们的程序就得崩。

为了解决这个问题,如果我们接收到的输入是不可用的,就要返回一个 nil,这比返回一个无内容的或者错误的 ListItem 来导致拆包 NSURL 时崩溃要好得多。

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
}

但是我们现在 jsonItems.map 里面传入的参数函数的类型为 NSDictionary -> ListItem?,最后我们得到的是一个 [ListItem?] 数组,那些原来是不可用 NSDictionary 的位置就被我们替换成了 nil。比原来要好一些了,但还不够。

使用flatMap()

这个时候就轮到 flatMap() 来救场了。

flatMap()map() 相似,但 flatMap() 用的是 T->U? 转化而不是 T->U 转化,而且如果转化出的数组元素是 nil 的话,就不会被添加到最后的结果数组里面。

在语法上,你可以这么理解,flatMap 就是你先使用 map 然后把结果数组“压平”(毕竟函数名就是这个意思),也就是从输出数组里去掉那些 nil

如果使用了这个方法,代码就会变成这个样子:

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
}

现在我们只返回所有键都存在的 ListItem (也就是我们保证 NSURL是不为空的)。那些不可用的元素早在我们执行 guard 语句通知 flatMap 之后,就被从输出数组中去掉了。

这样做就更好更安全了吧,我们解决了错误输入数据和得到无内容实例的问题。

结论

我们仍然有很多工作要做,但是今天就先做这些吧(让我们为本系列文章的下一篇准备一下材料!)

在这篇文章里面,我们学到了怎么用 map 或者 flatMap 来替换掉 for 循环,我们成功保证了即便输入数据是不可用的的情况下,我们的输出数组也不会出问题。这确实已经算是很大的进步了。

在下一篇文章里,我们将学到把我们的 ListItem 转化成一个 struct 之后会带来什么样的好处,并且探索 mapflatMap 的其它用法 – 尤其是在处理 Optionals 的时候。

同时,我们希望你花点时间来深入了解一下 map()flatMap() 在数组上的应用,我知道你第一次学的时候可能觉得它们很复杂,但是一旦你学会了,你什么时候都会想用它们。

以 Swift 的方式思考,第二部分:调用数组的 Map 方法的更多相关文章

  1. html5使用canvas实现弹幕功能示例

    这篇文章主要介绍了html5使用canvas实现弹幕功能示例的相关资料,需要的朋友可以参考下

  2. HTML5 WebSocket实现点对点聊天的示例代码

    这篇文章主要介绍了HTML5 WebSocket实现点对点聊天的示例代码的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  3. 前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)

    这篇文章主要介绍了前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. H5 canvas实现贪吃蛇小游戏

    本篇文章主要介绍了H5 canvas实现贪吃蛇小游戏,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

  6. ios – 声明NSDictionary并在Swift中添加键值对?

    我一直在尝试使用类类型键和值来声明一个NSDictionary,如下所示:这里,“Category”和“SubCategory”是全局类.我知道我不能将类类型用于关键字段.但是,无论如何,我应该做到这一点.有没有办法做到这一点?如何声明专门的NSDictionary或类似的东西来做到这一点?

  7. ios – parse.com用于键,预期字符串的无效类型,但是得到了数组

    我尝试将我的数据保存到parse.com.我已经预先在parse.com上创建了一个名为’SomeClass’的类.它有一个名为’mySpecialColumn’的列,其数据类型为String.这是我尝试使用以下代码保存数据的代码:如果我运行这个我得到:错误:密钥mySpecialColumn的无效类型,预期字符串,但得到数组这就是我在parse.com上的核心外观:有谁知道我为什么会收到这个错误?

  8. ios – 在Swift中将输入字段字符串转换为Int

    所以我非常擅长制作APP广告Swift,我试图在文本字段中做一些非常简单的输入,取值,然后将它们用作Int进行某些计算.但是’vardistance’有些东西不正确它是导致错误的最后一行代码.它说致命错误:无法解开Optional.None解决方法在你的例子中,距离是一个Int?否则称为可选的Int..toInt()返回Int?因为从String到Int的转换可能失败.请参阅以下示例:

  9. ios – Swift相当于`[NSDictionary initWithObjects:forKeys:]`

    Swift的原生字典是否与[NSDictionaryinitWithObjects:forKeys:]相当?假设我有两个带键和值的数组,并希望将它们放在字典中.在Objective-C中,我这样做:当然我可以通过两个数组迭代一个计数器,使用vardict:[String:Int]并逐步添加东西.但这似乎不是一个好的解决方案.使用zip和enumerate可能是同时迭代两者的更好方法.然而,这种方法

  10. ios – NSJSONSerialization崩溃的应用程序

    我有一本字典,当我记录它时显示…我正试图将它变成NSData,用于像这样的JSONWeb服务……但每次我运行它时,应用程序都会崩溃.字典正确形成,应用程序只是崩溃在这一行.在AppCode中,我收到崩溃报告……在Xcode中,应用程序停止,如果我尝试继续,它会因错误而停止…

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部