Swift 1- map / flatMap / compact

2019-12-30  本文已影响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 语句, 对那些只有解包成功的元素,才会添加到结果集中。

上一篇下一篇

猜你喜欢

热点阅读