對(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都可以用在Optionals和SequenceTypes上(如:數(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]
