美文网首页
Swift 1- map / flatMap / compact

Swift 1- map / flatMap / compact

作者: 暗物质 | 来源:发表于2019-12-30 18:17 被阅读0次

    map 简单使用:

    let numbers = [1,2,3,4]
    let result = numbers.map { $0 + 2 }
    print(result)  // [3,4,5,6]
    
    let cast = ["Vivien", "Marlon", "Kim", "Karl"]
    let lowercaseNames = cast.map { $0.lowercased() }
    // 'lowercaseNames' == ["vivien", "marlon", "kim", "karl"]
    let letterCounts = cast.map { $0.count }
    // 'letterCounts' == [6, 6, 3, 4]
    
    let stringResult = numbers.map { "No. \($0)" }
    // ["No. 1", "No. 2", "No. 3", "No. 4"]
    

    flatMap 使用:

    let numbersCompound = [[1,2,3],[4,5,6]];
    var res = numbersCompound.map { $0.map{ $0 + 2 } }
    // [[3, 4, 5], [6, 7, 8]]
    var flatRes = numbersCompound.flatMap { $0.map{ $0 + 2 } }
    // [3, 4, 5, 6, 7, 8]
    

    可以看到 flatMapmap 不同, 它对最终的结果进行了所谓的 “降维” 操作。 本来原始数组是一个二维的, 但经过 flatMap 之后,它变成一维的了。

    为什么 flatMap 调用后会对数组降维呢? 我们可以从它的源码中窥探一二 (文件位置: 源码

    //===----------------------------------------------------------------------===//
    // flatMap()
    //===----------------------------------------------------------------------===//
    extension Sequence {
      /// Returns an array containing the concatenated results of calling the
      /// given transformation with each element of this sequence.
      ///
      /// Use this method to receive a single-level collection when your
      /// transformation produces a sequence or collection for each element.
      ///
      /// In this example, note the difference in the result of using `map` and
      /// `flatMap` with a transformation that returns an array.
      ///
      ///     let numbers = [1, 2, 3, 4]
      ///
      ///     let mapped = numbers.map { Array(repeating: $0, count: $0) }
      ///     // [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]
      ///
      ///     let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) }
      ///     // [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
      ///
      /// In fact, `s.flatMap(transform)`  is equivalent to
      /// `Array(s.map(transform).joined())`.
      ///
      /// - Parameter transform: A closure that accepts an element of this
      ///   sequence as its argument and returns a sequence or collection.
      /// - Returns: The resulting flattened array.
      ///
      /// - Complexity: O(*m* + *n*), where *n* is the length of this sequence
      ///   and *m* is the length of the result.
      @inlinable
      public func flatMap<SegmentOfResult: Sequence>(
        _ transform: (Element) throws -> SegmentOfResult
      ) rethrows -> [SegmentOfResult.Element] {
        var result: [SegmentOfResult.Element] = []
        for element in self {
          result.append(contentsOf: try transform(element))
        }
        return result
      }
    }
    
    

    源码中对遍历的每一个元素调用 try transform(element)。 transform 函数就是我们传递进来的闭包。
    然后将闭包的返回值通过result.append(contentsOf:)函数添加到 result 数组中。
    那我们再来看一下result.append(contentsOf:)都做了什么, 它的文档定义是这样:
    Append the elements of newElements to self.
    简单说就是将一个集合中的所有元素,添加到另一个集合。

    let numbersCompound = [[1,2,3],[4,5,6]];
    var flatRes = numbersCompound.flatMap { $0.map{ $0 + 2 } }
    // [3, 4, 5, 6, 7, 8]
    

    flatMap 首先会遍历这个数组的两个元素 [1,2,3] 和 [4,5,6], 因为这两个元素依然是数组, 内部的$0.map{ $0 + 2 } 调用返回值类型还是数组, 它会返回 [3,4,5] 和 [6,7,8]。
    然后, flatMap 接收到内部闭包的这两个返回结果, 进而调用 result.append(contentsOf:) 将它们的数组中的内容添加到结果集中,而不是数组本身。
    那么我们最终的调用结果理所当然就应该是 [3, 4, 5, 6, 7, 8] 了。

    compactMap 使用

    let optionalArray: [String?] = ["AA", nil, "BB", "CC"]
    print(optionalArray)// [Optional("AA"), nil, Optional("BB"), Optional("CC")]
    let optionalResult = optionalArray.compactMap{ $0 }
    print(optionalResult) // ["AA", "BB", "CC"]`
    
    let numbersCompound: [[Int?]] = [[1,2,nil],[4,5,6]];
    let dd = numbersCompound.compactMap { $0.compactMap { $0 }.compactMap{ $0+2 } }
    print(dd) //[[3, 4], [6, 7, 8]]
    

    compactMap的返回结果中, 成功的将原数组中的 nil 值过滤掉了。 使用 compactMap 调用之后, 数组中的所有元素都被解包了。

    那么 compactMap 是如何实现过滤掉 nil 值的呢? 我们还是来看一下源码:

    extension Sequence {
      /// Returns an array containing the non-`nil` results of calling the given
      /// transformation with each element of this sequence.
      ///
      /// Use this method to receive an array of non-optional values when your
      /// transformation produces an optional value.
      ///
      /// In this example, note the difference in the result of using `map` and
      /// `compactMap` with a transformation that returns an optional `Int` value.
      ///
      ///     let possibleNumbers = ["1", "2", "three", "///4///", "5"]
      ///
      ///     let mapped: [Int?] = possibleNumbers.map { str in Int(str) }
      ///     // [1, 2, nil, nil, 5]
      ///
      ///     let compactMapped: [Int] = possibleNumbers.compactMap { str in Int(str) }
      ///     // [1, 2, 5]
      ///
      /// - Parameter transform: A closure that accepts an element of this
      ///   sequence as its argument and returns an optional value.
      /// - Returns: An array of the non-`nil` results of calling `transform`
      ///   with each element of the sequence.
      ///
      /// - Complexity: O(*m* + *n*), where *n* is the length of this sequence
      ///   and *m* is the length of the result.
      @inlinable // protocol-only
      public func compactMap<ElementOfResult>(
        _ transform: (Element) throws -> ElementOfResult?
      ) rethrows -> [ElementOfResult] {
        return try _compactMap(transform)
      }
    
      // The implementation of flatMap accepting a closure with an optional result.
      // Factored out into a separate functions in order to be used in multiple
      // overloads.
      @inlinable // protocol-only
      @inline(__always)
      public func _compactMap<ElementOfResult>(
        _ transform: (Element) throws -> ElementOfResult?
      ) rethrows -> [ElementOfResult] {
        var result: [ElementOfResult] = []
        for element in self {
          if let newElement = try transform(element) {
            result.append(newElement)
          }
        }
        return result
      }
    }
    
    

    依然是遍历所有元素,并应用 try transform(element)闭包的调用, 但关键一点是,这里面用到了 if let 语句, 对那些只有解包成功的元素,才会添加到结果集中。

    相关文章

      网友评论

          本文标题:Swift 1- map / flatMap / compact

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