Swift之Map與CompactMap區(qū)別

對(duì)于一個(gè)iOS開發(fā),如何便利一個(gè)Array、Dictionary再熟悉不過了,for、for in嘛...
被某位前輩問到其中的map和flatMap區(qū)別時(shí),未答出個(gè)所以然,然后..然后就沒有然后了...



嗯嗯...聲音很響...
在項(xiàng)目空閑期,針對(duì)Swift中的Map、CompactMap、Filter、Reduce進(jìn)行系統(tǒng)學(xué)習(xí)

感謝:
化零為整:Reduce詳解
Swift 4.1 新特性 (2) Sequence.compactMap
Swift數(shù)組中Map,FlatMap,Filter,Reduce的使用
Swift 2.0 :揭秘 Map 和 FlatMap

被問到的兩個(gè)問題

問題1:將一個(gè)包含Student對(duì)象的數(shù)組,取出其中的name值,并生成一個(gè)新數(shù)組,如何實(shí)現(xiàn)?
struct Student {
    var name: String
    var age: Int
}

let student1 = Student(name: "張三", age: 23)
let student2 = Student(name: "李四", age: 24)
let student3 = Student(name: "王二", age: 25)

/// 可以看到下面的代碼正是我們經(jīng)常用到的代碼,通過創(chuàng)建一個(gè)中間變量來接收
func getNames(students: [Student]) -> [String] {
    var names = [String]()
    for student in students {
        names.append(student.name)
    }
    return names
}

/// 通過Map便利,我們可以不再定義可變的數(shù)組來做中間變量,并且代碼更簡(jiǎn)潔
func getNames1(students: [Student]) -> [String] {
    return students.compactMap {
        $0.name
    }
}

print(getNames(students: [student1, student2, student3]))
print(getNames1(students: [student1, student2, student3]))
/// 均輸出["張三", "李四", "王二"]
問題2:Map和FlatMap的區(qū)別?

對(duì)于不同點(diǎn),我們先說說相同點(diǎn):
Map和FlatMap都可以用在OptionalsSequenceTypes上(如:數(shù)組、字典等)。

對(duì)于不同點(diǎn):
先說說Swift4新加入的新特性compactMap;
flatMap會(huì)將transform函數(shù)的返回類型先拍扁,在組合成本身的復(fù)合類型,標(biāo)準(zhǔn)庫有3個(gè) flatMap

Sequence.flatMap<S>(_: (Element) -> S) 
    -> [S.Element] where S : Sequence
Optional.flatMap<U>(_: (Wrapped) -> U?) -> U?
Sequence.flatMap<U>(_: (Element) -> U?) -> [U]

在 Swift 4.1中引入的compactMap新函數(shù)實(shí)際上是對(duì)第三個(gè)方法的重命名。

1. Sequence.flatMap區(qū)別

map 最終將它們組成一個(gè)二維的數(shù)組;
flatMap 中執(zhí)行的 closure 返回的是同樣的數(shù)組,但是 flatMap 將每一個(gè)返回的數(shù)組都拍扁,取出它的元素,構(gòu)成一個(gè)大的一維數(shù)組。

let numbers = [[1, 2, 3, 4], [5, 6], [7]]

let maped = numbers.map { $0 }
let flatMapped = numbers.flatMap { $0 }

print(maped)
print(flatMapped)
/// [[1, 2, 3, 4], [5, 6], [7]]
/// [1, 2, 3, 4, 5, 6, 7]



2. Optional.flatMap區(qū)別

Optional的版本知道的和日常使用的就沒有那么多了。先仔細(xì)看一下這兩個(gè)函數(shù)的定義,區(qū)別僅在一個(gè)處:

func map<U>( transform: (Wrapped) -> U)  -> U?
func flatMap<U>( transform: (Wrapped) -> U?) -> U?

map是閉包內(nèi)return為非可選項(xiàng),但最終返回值為可選項(xiàng),即閉包return后又套了一層可選項(xiàng)。
flatMap是閉包內(nèi)return為可選項(xiàng),最終返回值也為可選項(xiàng),即閉包內(nèi)return后對(duì)可選項(xiàng)進(jìn)行解包,最終又套了一層可選項(xiàng)。

說了區(qū)別,還是通過一個(gè)例子來說明:

let number: Int? = Int("2")

let mapSquare = number.map { (x) -> Int in
    // 此處編譯不通過
    // Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
    // 閉包內(nèi)return非可選項(xiàng)
    return x % 2 == 0 ? 0 : x
}

let flatMapSquare = number.flatMap { (x) -> Int? in
    return x % 2 == 0 ? nil : x
}

那么什么時(shí)候使用 Optional Chaining,什么時(shí)候使用 Optional 的 Mapping 呢?

看了小得寫代碼的文章,自覺其說的有一定道理
我認(rèn)為,他們的差異在于 receiver 在類型轉(zhuǎn)換過程中承擔(dān)的角色,Optional Chaining 中只能作為 receiver,而 Optional Mapping 函數(shù)還能作為參數(shù),更為通用,因而第一個(gè)例子只能用 Mapping 實(shí)現(xiàn);另一個(gè)方面,Optional Chaining 只能寫成串聯(lián)表達(dá)式,而 closure 當(dāng)中可以寫更復(fù)雜的轉(zhuǎn)換邏輯,還是用例子來說明:

let possibleNumber: Int? = Int("42")
let nonOverflowingSquare = possibleNumber.flatMap { 
    x -> Int? in
    let (result, overflowed) = x.multipliedReportingOverflow(by: x)
    return overflowed ? nil : result
}



2. Optional.compactMap區(qū)別

眾所周知,flatMap可以作為降維使用,除了 flat 之外其實(shí)還有 filter 的作用,在使用時(shí)容易產(chǎn)生歧義,所以社區(qū)認(rèn)為最好把第二個(gè)版本重新拆分出來,使用一個(gè)新的方法命名,就產(chǎn)生了這個(gè)提案 SE-0187

最初這個(gè)提案用了filterMap這個(gè)名字,但后來經(jīng)過討論,就決定參考了 Ruby 的Array::compact方法,使用compactMap這個(gè)名字。

如下是改版前后的代碼區(qū)別,可以看到用法相同,僅針對(duì)名字進(jìn)行了更改。

Sequence.flatMap<U>(_ : (Element) -> U?) -> [U]
Sequence.compactMap<U>(_ : (Element)  -> U?) -> [U]

compactMap,可以把一個(gè)集合中的空值去除,并且返回一個(gè)去除nil值得數(shù)組
下面通過代碼例子說明下區(qū)別:

let numbers = ["1", "2", "three", "http:///5///", "5"]

let mapResult = numbers.map { (number) -> Int? in
    return Int(number)
}
print(mapResult)
// [Optional(1), Optional(2), nil, nil, Optional(5)]

let compactMapResult = numbers.compactMap { (number) -> Int? in
    return Int(number)
}
print(compactMapResult)
// [1, 2, 5]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 首先在腦海里回想一下 map 和 flattenMap 的區(qū)別。flattenMap 會(huì)將 transform 函...
    面試官小健閱讀 5,623評(píng)論 0 7
  • 一、數(shù)組中的 map 和 flatMap 數(shù)組中的 map 對(duì)數(shù)組元素進(jìn)行某種規(guī)則的轉(zhuǎn)換,例如: 二、 flatM...
    kmplayer閱讀 4,196評(píng)論 0 6
  • 大年初三,露天游玩的人們時(shí)不時(shí)都會(huì)縮下脖子,風(fēng)很大,天好冷。 中午,趁著有太陽,孩她爸領(lǐng)著我家閨女上游樂場(chǎng)玩了趟,...
    張新樂閱讀 275評(píng)論 1 1
  • 現(xiàn)在是個(gè)快節(jié)奏的信息時(shí)代!微信在短短幾年時(shí)間幾乎成了每個(gè)人的生活方式,關(guān)鍵一點(diǎn),在于其微!既能互動(dòng)交流,又能自娛自...
    良辰美景5456閱讀 282評(píng)論 0 0
  • 4.14,這一天真的到了。在之前的五個(gè)月里,科比是打一場(chǎng)少一場(chǎng),而在今天,科比真的是打一分鐘就少一分鐘。 這一天,...
    Amazing_初閱讀 889評(píng)論 4 11

友情鏈接更多精彩內(nèi)容