swift之map和flatMap

一、Optional的map和flatMap

Optional 的定義

public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    //我們在??寫代碼的時候,習慣用 nil 來表示空值,而不是 .none
    case none

    //非空的值用 Wrapped 來表示
    case some(Wrapped)

    //optional只有一個map函數(shù),無?其它變體
    public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?  {
        switch self {
            case .some(let y):
            return .some(try transform(y))
            case .none:
            return .none
        }
    }

    //optional只有一個flatMap函數(shù),無?其它變體
    public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?{
        switch self {
            case .some(let y):
                return try transform(y)
            case .none:
                return .none
        }
    }

map 和 flatMap的共性

map和flatMap函數(shù)的返回結果依舊是可選性;但返回的可選值類型可以與原可選值類型不一致。

  • 如果?原optional沒有值, map和flatMap函數(shù)都直接?返回nil。
  • flatMap?:如果原optional有值?非空,則用解包后的值y做參數(shù)來?調(diào)用閉包transform,閉包的結果?就是flatMap的結果,transform(y)? ???降維。
  • map:如果原optional有值?非空,則用解包后的值y做參數(shù)來?調(diào)用閉包transform,map的返回值是 .some( transform(y) )

例如:

// flatMap降維和 map的對比示例
let a: String? = "1"
let am = a.map{ Int($0) }       // 閉包結果類型 Int?    
print( am )  // Optional(Optional(1))  map結果類型 Int??  .some( transform(y) )

let afm = a.flatMap{ Int($0) }   // 閉包結果類型 Int? 
print(afm)  //  Optional(1)    flatMap結果類型 Int??  transform(y)?
// 原來類型:Int?, 返回值類型:String?
let b: Int? = 1
let bm = b.map { String("result = \($0)") }   // 閉包結果類型 String
print(bm)   /// "Optional("result = 1")"

// 原optional沒有值, map和flatMap函數(shù)都直接?返回nil
let c:Int? = nil            
let cm = c.map { String("result = \($0)") }   // 閉包結果類型 String
print(bm)   /// "nil"       map結果類型 String? 

結論:那什么時候使用flatMap 方法呢?答案是:當我們的閉包transform(y)的??返回值?是可選型的??時候,可以用f?latMap來降一次維

二、Sequence 的? flatMap: 去空降維

每調(diào)用一次flatMap就去一次空、或降一次維

  • 去空:把空值nil過濾掉。
  • 降維:比如把?二維數(shù)組壓平成一維數(shù)組。
  • 每次調(diào)用只能2選一(去空、或降維),?這2個功能各自對應一個不同的重載flatMap函數(shù)。
extension SequenceType {
    // 去空:把空值nil過濾。 
    public func flatMap<T>(transform: (Self.Generator.Element) -> T?)   ->  [T]

    // 降維:比如把二維數(shù)組壓平成一維數(shù)組
    public func flatMap<S : SequenceType>(transform: (Self.Generator.Element) -> S)  ->  [S.Generator.Element]
// [Int?]  ->  [ Int ]   去空
var a = [1, 3, 5, nil, 7, 9]             // a 類型 [Int?]
let afm = a.flatMap{ $0 }             // afm 類型是 [ Int ]
print(type(of: afm), ": ", afm)    /// [1, 3, 5, 7, 9]


// [ [Int] ] -> [ Int ]  降維
var b = [[1, 3, 5], [7, 9] ]          // b 類型 [ [Int] ]
let bfm = b.flatMap{ $0 }           // bfm 類型 [ Int ]
print(type(of: bfm),": ", bfm)  /// [1, 3, 5, 7, 9 ]


// [ [Int]? ] -> [ [Int] ]  去空
var c = [ [1, 3, 5], [7, 9], nil ]  // c 的類型是 [ [Int]? ]
let cfm = c.flatMap{ $0 }           // cfm 類型  [ [Int] ]
print(type(of: cfm),": ", cfm)  /// [[1, 3, 5], [7, 9] ]


// [ [Int?] ]  -> [ Int? ]   降維
var d = [ [1, 3, 5], [7, 9], [nil] ]  // c 的類型是 [ [Int?] ]
let dfm = d.flatMap{ $0 }              // dfm 類型 [ Int? ]
print(type(of: dfm),": ", dfm) /// [1, 3, 5, 7, 9, nil ]


// [ [Int?]? ]  -> [ [Int?] ]  去空
var e = [ [1, 3, 5], [7, 9], [nil],  nil ]  // c 的類型是 [ [Int?]? ]
let efm = e.flatMap{ $0 }                    // efm 類型 [ [Int?] ]
print(efm) /// [[1, 3, 5], [7, 9], [nil], [10] ]

結論: 最好在操作 flatMap 時, 最好顯示聲明類型,這樣可以更清晰的知道應該調(diào)用多少次flatMap,如:

// [ [Int?]? ]  -> [ Int ]  一看就知道要調(diào)用3次。
var f:  [ [Int?]? ] = [ [1, 3, 5], [7, 9], [nil],  nil ]         // c 的類型是 [ [Int?]? ]
let ffm3: [ Int ] = f.flatMap{ $0 }.flatMap{ $0 }.flatMap{ $0 }   // efm 類型 [ Int ]
print(ffm3) /// [1, 3, 5, 7, 9]
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容