美文网首页
Swift集合类高阶函数

Swift集合类高阶函数

作者: 小笨憨 | 来源:发表于2019-07-31 14:28 被阅读0次

    在Swift的集合类型中,有许多十分便捷的函数。相比于Objective-C,这些高阶函数会引起你的极度舒适。因为在Swift的许多函数中引入了闭包元素,这就直接造就了它的灵活性,简直就是极致的便捷。

    下面就来对Swift集合类中的这些高阶函数进行总结。

    // 全文的基础数据
    let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
    
    1.sort函数

    对原集合进行给定条件排序。
    无返回值,直接修改原集合,所以这个集合应该是可变类型的。

    var sortArr = numbers
    numbers.sort { a, b in
        return a < b
    }       
    // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    

    另外,系统还定义了一个sort()函数,即对集合进行升序排序的函数。但这个函数并不是上面函数不传入缺省值的情况,而是另外一个函数。

    var sortArr2 = numbers
    numbers.sort()      
    // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    2.sorted函数

    sorted函数sort函数对应。
    将集合进行给定条件排序,返回一个新的集合,不修改原集合。

    let sortedArr = numbers.sorted { a, b in
        return a > b
    }
    // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    
    // sorted()函数
    let sortedArr2 = numbers.sorted()
    // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    
    // 闭包简写
    let sortedArr3 = sortedArr2.sorted(by: >)
    // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    
    闭包的省略写法

    因为在高阶函数中大部分都使用了闭包,所以我认为有必要做一个铺垫,以更好地理解本文。清楚闭包简写的请跳过本段,直奔第3条

    由于sort函数使用了闭包,所以自主定义的闭包可以简写为如下格式:

    numbers.sort(by: >)     
    // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    

    以上述方法为例,一个完整的闭包应该是这样的:

    numbers.sorted { (a: Int, b: Int) -> Bool in
        return a > b
    }
    

    然后,可以省略闭包中的返回值。

    numbers.sorted { (a: Int, b: Int) in
        return a > b
    }
    

    然后,再可以省略形参的类型,让编译器去自主推断。

    numbers.sorted { a, b in
        return a > b
    }
    

    再然后,还可以让$0$1…来代替第一个,第二个形参,以此类推。

    numbers.sorted { return $0 > $1 }
    

    再然后,省略return。一般的,到这里也就足够简化了。毕竟在实际开发中我们需要使用闭包中的参数进行一些复杂的判断。

    numbers.sorted { $0 > $1 }
    

    如果你不需要复杂的判断,那么还可以写成下面这样,代表降序排序。

    numbers.sorted(by: >)
    
    3.map函数

    按照闭包中的返回结果,将集合中对应元素进行替代,也就是映射函数。

    // 数组数值转换为其各自平方
    let mapArr = numbers.map { $0 * $0 }
    // [49, 36, 100, 81, 64, 1, 4, 9, 16, 25]
    
    可选类型的 map, flatMap函数

    另外,不仅CollectionTypemapflatMap函数,在Optional类型中,也存在这两个函数。
    它们的作用是对可选类型就行解包操作,若有值则进入闭包,并返回一个 Optional类型;若为nil,则直接返回当前可选类型的nil

    let num1: Int? = 3
    let num2: Int? = nil
    
    let numMap1 = num1.map {
        $0 * 2
    }
    numMap1              // 6
    type(of: numMap1)    // Optional<Int>.Type
    
    let numMap2 = num2.map {
        $0 == 0
    }
    numMap2              // nil
    type(of: numMap2)    // Optional<Bool>.Type
    
    
    let numFlatMap1 = num1.flatMap {
        $0 * $0
    }
    numFlatMap1              // 9
    type(of: numFlatMap1)    // Optional<Int>.Type
    
    let numFlatMap2 = num2.flatMap {
        $0 == 0
    }
    numFlatMap2              // nil
    type(of: numFlatMap2)    // Optional<Bool>.Type
    

    还有一种应用场景,就是解析可选类型的时候,mapflatMap函数会让你的代码更加优雅。

    举个例子,当解析并判断可选类型的时候,你可能会经过一堆if或者guard判断,如下所示:

    func loadURL(url: URL) {
        print(url.absoluteString)
    }
    
    let urlStr: String? = "https://github.com/wangyanchang21"
    guard let siteStr = urlStr else {
        assert(false)
    }
    guard let url = URL(string: siteStr) else {
        assert(false)
    }
    loadURL(url: url)
    

    如果使用mapflatMap函数的话,就会有十分优雅的感觉。

    // 这行优雅的代码代替上面的代码
    urlStr.flatMap(URL.init).map(loadURL)
    

    但有一点需要注意,这里 map替换 flatMap会报错, 原因在于 flatMap闭包可以返回 nil, 而 map闭包不可以。就如下面的代码编译不会通过:

    // compile error
    // urlStr.map(URL.init).map(loadURL)
    

    再举一个例子:

    let date: Date? = Date()
    let format = date.map(DateFormatter().string)
    

    我在函数的闭包形式中也写过这种优雅的写法具体是怎么回事。有兴趣可以了解一下。

    4.flatMap函数

    也是一种映射函数,这个函数具有多重功能,所以也就造成了这个函数有一个历史问题,稍后会解释。

    第一种情况,解析首层元素,若有nil则过滤,就不会降维

    let optLatticeNumbers = [[1, Optional(2), 3], [3, nil, 5], nil]
    // 解析首层元素, 若有nil则过滤, 就不会降维
    let flatMapArr2 = optLatticeNumbers.flatMap { $0 }
    // [[1, 2, 3], [3, nil, 5]]
    

    第二种情况,解析首层元素,若没有nil,则会降维

    let latticeNumbers = [[1, Optional(2), 3], [3, nil, 5]]
    // 解析首层元素, 若没有nil, 则会降维
    let flatMapArr = latticeNumbers.flatMap { $0 }
    // [1, 2, 3, 3, nil, 5]
    

    所以flatMap的功能就有两个了,一个功能是解析并过滤首层元素为nil的元素,一个功能是对多维集合进行降维。原因是,其实这是两个功能是两个函数,只是在调用时代码上没有区别。

    flatMap和compactMap的关系

    但从表面上看,flatMap函数违背了单一功能原则,将过滤nil降维两个功能于隐藏条件中进行判定。这也就是那个历史问题。

    因此,为了将过滤nil和降维两个功能于区分开,swift4.1开始,就只保留了降维的flatMap函数,并弃用了过滤nilflatMap函数,又用开放的新函数compactMap来替代弃用的函数。

    所以,当需要过滤nil的时候,请使用compactMap函数;当需要进行降维时,请使用flatMap函数。这也就是flatMapcompactMap之间的区别。

    5.compactMap函数

    Swift4.1开始开放的一种映射函数,会解析并过滤首层元素为nil的元素。

    let compactMapArr = optLatticeNumbers.compactMap { $0 }
    // [[1, 2, 3], [3, nil, 5]]
    let compactMapArr2 = latticeNumbers.compactMap { $0 }
    // [[1, 2, 3], [3, nil, 5]]
    

    compactMap函数作为过滤nilflatMap函数的替代函数。当集合中的元素为一个一维集合,他们之间的功能是没有差别的。

    let flatNumbers = [1, Optional(2), 3, nil, Optional(5), nil]
    
    let flatMapArr = latticeNumbers.flatMap { $0 }
    // [1, 2, 3, 5]
    let compactMapArr = optLatticeNumbers.compactMap { $0 }
    // [1, 2, 3, 5]
    
    6.filter函数

    按照条件进行元素过滤。

    let filterArr = numbers.filter { num in
        return num < 3 || num > 8
    }
    // [10, 9, 1, 2]
    
    7.reduce函数

    以指定参数为基础,按照条件进行拼接

    let reduceNumber = numbers.reduce(100) { result, num in
        return result + num
    }
    // 155
    
    let reduceString = ["C", "O", "D", "E"].reduce("word: ") { result, num in
        return result + num
    }
    // "word: CODE"
    
    8.prefix函数

    正向取满足条件的元素,进行新集合创建。一旦出现不满足条件的元素,则跳出循环,不再执行。

    let prefixArr = numbers.prefix { $0 < 10 }
    // [7, 6]
    

    prefix相关函数:
    upTo: 正向取元素创建数组, 包含小于指定index的元素

    let prefixUpToArr = numbers.prefix(upTo: 5)
    // [7, 6, 10, 9, 8]
    

    through: 正向取元素创建数组, 包含小于等于指定index的元素

    let prefixThroughArr = numbers.prefix(through: 2)
    // [7, 6, 10]
    

    maxLength: 正向取元素创建数组, 包含指定的元素个数

    let prefixMaxLengthArr = numbers.prefix(6)
    // [7, 6, 10, 9, 8, 1]
    
    9.drop函数

    prefix函数对应。正向跳过满足条件的元素,进行新集合创建。一旦出现不满足条件的元素,则跳出循环,不再执行。

    let dropArr = numbers.drop { $0 < 10 }
    // [10, 9, 8, 1, 2, 3, 4, 5]
    

    drop相关函数:
    dropFirst: 正向跳过元素创建数组, 跳过指定元素个数, 缺省值为1

    let dropFirstArr = numbers.dropFirst(3)
    // [7, 6, 10, 9, 8]
    

    dropLast: 返向跳过元素创建数组, 跳过指定元素个数, 缺省值为1

    let dropLastArr = numbers.dropLast(5)
    // [7, 6, 10, 9, 8]
    
    10.first函数

    正向找出第一个满足条件的元素。

    let first = numbers.first { $0 < 7 }
    // 6
    
    11.last函数

    first函数对应。反向找出第一个满足条件的元素。

    let last = numbers.last { $0 > 5 }
    // 8
    
    12.firstIndex函数

    正向找出第一个满足条件的元素下标。

    let firstIndex = numbers.firstIndex { $0 < 7 }
    // 1
    
    13.lastIndex函数

    反向找出第一个满足条件的元素下标。

    let lastIndex = numbers.lastIndex { $0 > 5 }
    // 4
    
    14.partition函数

    按照条件进行重新排序,不满足条件的元素在集合前半部分,满足条件的元素后半部分,但不是完整的升序或者降序排列。
    返回值为排序完成后集合中第一个满足条件的元素下标。

    var partitionNumbers = [20, 50, 30, 10, 40, 20, 60]
    let pIndex = partitionNumbers.partition { $0 > 30 }
    // partitionNumbers = [20, 20, 30, 10, 40, 50, 60]
    // pIndex = 4
    
    15.min函数

    按条件排序后取最小元素。

    let min = numbers.min { $0 % 5 < $1 % 5 }
    // 10
    

    min()函数,自然升序取最小。

    let minDefault = numbers.min()
    // 1
    
    16.max函数

    按条件排序后取最大元素。

    let maxDictionary = ["aKey": 33, "bKey": 66, "cKey": 99]
    let max = maxDictionary.max { $0.value < $1.value }
    // (key "cKey", value 99)
    

    max()函数,自然升序取最大。

    let maxDefault = numbers.max()
    // 10
    
    17.removeAll函数

    移除原集合中所有满足条件的元素。
    无返回值,直接修改原集合,所以这个集合应该是可变类型的。

    var removeArr = numbers
    removeArr.removeAll { $0 > 6 }
    // [6, 1, 2, 3, 4, 5]
    
    18.集合遍历

    forEach函数:

    numbers.forEach { num in
        print(num)
    }
    

    for-in函数:

    for num in numbers where num < 5 {
        print(num)
    }
    

    enumerated()函数配合使用:

    for (index, num) in numbers.enumerated() {
        print("\(index)-\(num)")
    }
    

    关于集合遍历的性能问题,可以看这里enumerated() 和 enumerateObjectsUsingBlock

    19.shuffled函数

    shuffled函数,打乱集合中元素的的顺序。

    let ascendingNumbers = 0...9
    let shuffledArr = ascendingNumbers.shuffled()
    // [3, 9, 2, 6, 4, 5, 0, 1, 7, 8]
    
    20.contains函数

    contains函数,判断集合中是否包含某元素。

    let containsBool = numbers.contains(8)
    let containsBool1 = numbers.contains(11)
    // true
    // false
    
    21.split和joined函数

    split函数,字符串的函数,按条件分割字符串,为子字符串创建集合。与Objective-C中的componentsSeparatedByString:方法类似。

    let line = "123Hi!123I'm123a123coder.123"
    let splitArr = line.split { $0.isNumber }
    // ["Hi!", "I'm", "a", "coder."]
    
    // 也可指定字符
    let splitArr2 = line.split(separator: "1")
    // ["23Hi!", "23I'm", "23a", "23coder.", "23"]
    

    joined函数,数组元素连接指定字符拼接成一个字符串。与Objective-C中的componentsJoinedByString:方法类似。

    let joined = splitArr.joined(separator: "_")
    // "Hi!_I'm_a_coder."
    
    // 也可以只传入字符
    let joined2 = splitArr2.joined(separator: "#")
    // "23Hi!#23I'm#23a#23coder.#23"
    
    22.zip函数

    将两个数组合并为一个元组组成的数组。

    let titles = ["aaa", "bbb", "ccc"]
    let numbers = [111, 222, 333]
    let zipA = zip(titles, numbers)
    for (title, num) in zipA {
        print("\(title)-\(num)")
    }
    

    打印结果:

    aaa-111
    bbb-222
    ccc-333
    

    相关文章

      网友评论

          本文标题:Swift集合类高阶函数

          本文链接:https://www.haomeiwen.com/subject/wmvvrctx.html