• 在第二部分,我将会精简我们的代码来让它看起来更 “Swift”,同时向你介绍 map() flatMap() 方法。 今天这篇文章我们将要讨论数组的 map() flatMap() 方法。 本系列文章的第一篇里这是我们上次留下的代码。...
    在[本系列文章的第一篇](http://alisoftware.github.io/swift/2015/09/06/thinking-in-swift-1/)中,我们知道了怎样避免对可选类型强制拆包,尽量少使用 `!` 也避免了程序崩溃。在第二部分,我将会精简我们的代码来让它看起来更 “Swift”,同时向你介绍 `map()` 和 `flatMap()` 方法。
    
    > 今天这篇文章我们将要讨论数组的 `map()` 和 `flatMap()` 方法。
    
    ###本系列文章的第一篇里
    这是我们[上次](http://alisoftware.github.io/swift/2015/09/06/thinking-in-swift-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” 的模式和语法来让我们的代码变得更简洁。
    
    ###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`。
    
    更糟的是,代码允许我们创建那些没有可用 `NSURL` 的 `ListItem()` 实例,而我们在 `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` 之后会带来什么样的好处,并且探索 `map` 和 `flatMap` 的其它用法 -- 尤其是在处理 `Optionals` 的时候。
    
    同时,我们希望你花点时间来深入了解一下 `map()` 和 `flatMap()` 在数组上的应用,我知道你第一次学的时候可能觉得它们很复杂,但是一旦你学会了,你什么时候都会想用它们。
    
    
    展开全文
  • Swift——map函数浅析 2015-10-27 00:20:51
    Swift语言的数组提供了一个map函数很好用,可建立一个a数组的映射数组b,即数学上的y = f(x). 我为大家用代码来实现一下: import Foundation //定义数组; var arr = [1,2,3,4,5] //需要对数组中的每一个值+10...
  • var list1 = ["number","name"];var list2 = ["36","Crown","15","Faker","Swift","68","Dandy"];var map_demo = { ...
  • swift 字符串的处理(map 2019-09-12 13:47:06
    let timeText = "2019-10-10 00:00:00,2019-10-10 00:00:00|2019-10-10 00:00:00,2019-10-10 00:00:00" let mapBlock: ([String]) -> (String) = { (list) -> String in var startTimeString = list.firs...
  • swift中高阶函数map、flatMap、filter、reduce Swift相比于Objective-C又一个重要的优点,它对函数式编程提供了很好的支持,Swift提供了map、filter、reduce这三个高阶函数作为对容器的支持。1 map:可以对数组中的...
  • js遍历listmap 2020-05-28 17:40:03
    var list2 = ["36","Crown","15","Faker","Swift","68","Dandy"]; var map_demo = { name: "John", lang: "JS" }; 1.最常用的for循环 for(var i=0;i<list2.length;i++){ console.info(i +":"+ list2 [i]); } ...
  • js遍历map和list 2020-01-07 10:47:16
    1、forEach遍历: ... map.forEach(function(value,key){  console.log(value,key);  });  函数中第一个参数是属性值,第二个参数是属性  2、for-of遍历:ES6新语法  ①for(let item of map){  }  ...
  • Swift之高阶函数map、flatMap、filter、reducemapflatMap与map不同之处filerreduce有关Swift.map高阶函数的应用   Swift相比于Objective-C又一个重要的优点,它对函数式编程提供了很好的支持,Swift提供了map、...
  • import UIKit /// 尾随闭包 func someFuncThatTakesClosure(closure: () -> Void) { } /// 不使用尾随闭包 someFuncThatTakesClosure(closure: { }) /// 使用尾随闭包 someFuncThatTakesClosure { ...
  • java的集合类主要由两个接口派生而出:Collection和Map Collection集合体系的继承树: Map体系的继承树:     java11为Collection新增了一个toArray(IntFunction)方法,使用该方法的主要目的...
  • 数组Java里面的数组,创建了以后的大小是不能改变的,只能往里面去存储数据,两个方式创建数组: String[] array = new String[10]; String[] array1 = {"aa","bb","cc"};我们创建了两个String数组,一个大小为10...
  • map中的remove方法 2020-06-02 14:42:49
    * Map移除集合 * */ public class MapDemo { public static void main(String[] args) { Map<String,Integer> m = new HashMap<String,Integer>(); m.put("zhangsan", 19); m.put("lisi", 49);...
  • List分组的两种方式 2018-10-30 21:27:03
    假设个student类,id、name、score属性,list集合中存放所有学生信息,现在要根据学生姓名进行分组。 public Map&lt;String, List&lt;Student&gt;&gt; groupList(List&lt;Student&gt; ...
  • 想从本地一个json文件取数据,...Cannot invoke ‘jsonObject’ with an argument list of type ‘(with: NSData, options: JSONSerialization.ReadingOptions)’let path = Bundle.main.path(forResource: "baseInfo
  • 这一话来讲一个...它表面上是AnyObject,但是在后台,它是NSString、NSArray、NSDictionary、NSNumer、NSDataNSDate这六种类型中的一种,或者它们桥接到Swift中的版本。我们为什么要用property list呢,它看起
  • STL中vector、list、deque和map 2016-07-07 19:44:34
    STL中vector、list、deque和map的区别 标签: listvectoriteratorstring数据结构存储 2012-03-28 17:50 25289人阅读 评论(6) 收藏 举报 本文章已收录于: 分类: C++(264) ...
  • // 不可变String数组 let names: String[] = ["name5", "name2", "name1", "name3"] //获取值 let name1 = names[0] //name5 let name2 = names[1] //name2 let na
  • Swift 4.0 新特性 2017-08-15 19:37:46
    WWDC 2017 带来了很多惊喜,在这次大会上,Swift 4 也伴随着 Xcode 9 测试版来到了我们的面前,虽然正式版要8月底9月初才会公布,但很多强大的新特性正吸引我们去学习它。根据大会上已经开放的新特性,先一睹为快。...
  • 最全的 Swift 4 新特性解析 2017-11-24 09:58:46
    因为 Swift 4 是开源的,如果你关注 swift-evolution 这个项目的话,就应该已经提前了解到它的新特性了。本文参考了 WWDC 2017 以及各种资料,从语法、字符串、标准库、构建过程等方面,把 Swift 4 的这些新特性一一
  • 一、变量声明 1、var var 用于声明变量,不用特指其类型。 var a; print(a);//null a = 10;...若初始化时未赋值,则变量的类型为dynamic(泛类型),之后可以给变量的赋值可以是任何类型 若初始化是已经赋值,则...
1 2 3 4 5 ... 20
收藏数 3,179
精华内容 1,271