日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Swift中的map 和 flatMap 原理及用法

發布時間:2024/1/4 综合教程 44 生活家
生活随笔 收集整理的這篇文章主要介紹了 Swift中的map 和 flatMap 原理及用法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

之前對這兩個概念有點糊,今天正好遇到一個相關需求,才深入了解了下。

需求如下:

大概就是對一個數組的model,重構成一個新model,返回得到一個新數組

用map很容易實現,不過后來我需要對其中進行一些過濾處理,這樣,用map就不行了,幸好,flatMap可以滿足我的需要。

其中原因歸納如下:

map是對原對象所有元素進行一對一轉換處理,中間不會跳過或遺漏,包括nil元素

flatMap更靈活,可變換維度,也能夠自動解包,所以當我們對不符合元素,返回nil,最終的結果是過濾掉nil的,從而能夠實現過濾。

以下是我網上看到的一篇描述比較詳細的文章,我就直接轉載過來了,想了解具體的童鞋可往下翻看。

原文地址:http://blog.csdn.net/fish_yan_/article/details/51785441

版權歸原作者所有

map 和 flatMap 是Swift中兩個常用的函數,它們體現了 Swift 中很多的特性。對于簡單的使用來說,它們的接口并不復雜,但它們內部的機制還是非常值得研究的,能夠幫助我們夠好的理解 Swift 語言。

map 簡介

首先,咱們說說 map 函數如何使用。

let numbers = [1,2,3,4]
let result = numbers.map { $0 + 2 }
print(result) // [3,4,5,6]

map 方法接受一個閉包作為參數, 然后它會遍歷整個 numbers 數組,并對數組中每一個元素執行閉包中定義的操作。 相當于對數組中的所有元素做了一個映射。 比如咱們這個例子里面的閉包是講所有元素都加 2 。 這樣它產生的結果數據就是 [3,4,5,6]。

初步了解之后,我們來看一下 map 的定義:

func map
(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]

咱們拋開一些和關鍵邏輯無關的修飾符 @noescape,throws 這些,在整理一下就是這樣:

func map
(transform: (Self.Generator.Element) -> T) rethrows -> [T]
 ```

map 函數接受一個閉包, 這個閉包的定義是這樣的:

(Self.Generator.Element) -> T
“`

它接受 Self.Generator.Element 類型的參數, 這個類型代表數組中當前元素的類型。 而這個閉包的返回值,是可以和傳遞進來的值不同的。 比如我們可以這樣:

let stringResult = numbers.map { "No. ($0)" }
// ["No. 1", "No. 2", "No. 3", "No. 4"]

這次我們在閉包裝把傳遞進來的數字拼接到一個字符串中, 然后返回一個組數, 這個數組中包含的數據類型,就是我們拼接好的字符串。

這就是關于 map 的初步了解, 我們繼續來看 flatMap。

flatMap

map 可以對一個集合類型的所有元素做一個映射操作。 那么 flatMap 呢?

讓我們來看一個 flatMap 的例子:

result = numbers.flatMap { $0 + 2 }
// [3,4,5,6]

我們對同樣的數組使用 flatMap 進行處理, 得到了同樣的結果。 那 flatMap 和 map 到底有什么區別呢?

咱們再來看另一個例子:

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]

這里就看出差別了。 對于二維數組, map 和 flatMap 的結果就不同了。 我們先來看第一個調用:

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

numbersCompound.map { … } 這個調用實際上是遍歷了這里兩個數組元素 [1,2,3] 和 [4,5,6]。 因為這兩個元素依然是數組,所以我們可以對他們再次調用 map 函數:$0.map{ $0 + 2 }。 這個內部的調用最終將數組中所有的元素加 2。

再來看看 flatMap 的調用:

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

flatMap 依然會遍歷數組的元素,并對這些元素執行閉包中定義的操作。 但唯一不同的是,它對最終的結果進行了所謂的 “降維” 操作。 本來原始數組是一個二維的, 但經過 flatMap 之后,它變成一維的了。

flatMap 是如何做到的呢,它的原理是什么,為什么會存在這樣一個函數呢? 相信此時你腦海中肯定會浮現出類似的問題。

下面咱們再來看一下 flatMap 的定義, 還是拋去 @noescape, rethrows 這些無關邏輯的關鍵字:

func flatMap(transform: (Self.Generator.Element) throws -> T?) -> [T]
func flatMap(transform: (Self.Generator.Element) -> S) -> [S.Generator.Element]

和 map 不同, flatMap 有兩個重載。 參照我們剛才的示例, 我們調用的其實是第二個重載:

func flatMa
p(transform: (Self.Generator.Element) -> S) -> [S.Generator.Element]

flatMap 的閉包接受的是數組的元素,但返回的是一個 SequenceType 類型,也就是另外一個數組。 這從我們剛才這個調用中不難看出:

numbersCompound.flatMap { $0.map{ $0 + 2 } }

我們傳入給 flatMap 一個閉包$0.map{ $0 + 2 }, 這個閉包中,又對 $0 調用了 map 方法, 從 map 方法的定義中我們能夠知道,它返回的還是一個集合類型,也就是 SequenceType。 所以我們這個 flatMap 的調用對應的就是第二個重載形式。

那么為什么 flatMap 調用后會對數組降維呢? 我們可以從它的源碼中窺探一二(Swift 不是開源了嗎~)。

文件位置: swift/stdlib/public/core/SequenceAlgorithms.swift.gyb

extension Sequence {
//...
public func flatMap(
@noescape transform: (${GElement}) throws -> S
) rethrows -> [S.${GElement}] {
var result: [S.${GElement}] = []
for element in self {
result.append(contentsOf: try transform(element))
}
return result
}
//...
}

這就是 flatMap 的完整源碼了, 它的源碼也很簡單, 對遍歷的每一個元素調用 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], 因為這兩個元素依然是數組, 所以我們可以對他們再進行 map 操作:$0.map{ $0 + 2 }

這樣, 內部的$0.map{ $0 + 2 }調用返回值類型還是數組, 它會返回 [3,4,5] 和 [6,7,8]。

然后, flatMap 接收到內部閉包的這兩個返回結果, 進而調用 result.append(contentsOf:) 將它們的數組中的內容添加到結果集中,而不是數組本身。

那么我們最終的調用結果理所當然就應該是 [3, 4, 5, 6, 7, 8] 了。

仔細想想是不是這樣呢~

flatMap 的另一個重載

我們剛才分析了半天, 其實只分析到 flatMap 的一種重載情況, 那么另外一種重載又是怎么回事呢:

func flatMap
(transform: (Self.Generator.Element) -> T?) -> [T]

從定義中我們看出, 它的閉包接收的是 Self.Generator.Element 類型, 返回的是一個 T? 。 我們都知道,在 Swift 中類型后面跟隨一個 ?, 代表的是 Optional 值。 也就是說這個重載中接收的閉包返回的是一個 Optional 值。 更進一步來說,就是閉包可以返回 nil。

我們來看一個例子:

let optionalArray: [String?] = ["AA", nil, "BB", "CC"];
var optionalResult = optionalArray.flatMap{ $0 }
// ["AA", "BB", "CC"]

這樣竟然沒有報錯, 并且 flatMap 的返回結果中, 成功的將原數組中的 nil 值過濾掉了。 再仔細觀察,你會發現更多。 使用 flatMap 調用之后, 數組中的所有元素都被解包了, 如果同樣使用 print 函數輸出原始數組的話, 大概會得到這樣的結果:

[Optional("AA"), nil, Optional("BB"), Optional("CC")]

而使用print函數輸出 flatMap 的結果集時,會得到這樣的輸出:

["AA", "BB", "CC"]

也就是說原始數組的類型是 [String?] 而 flatMap 調用后變成了 [String]。 這也是 flatMap 和 map 的一個重大區別。 如果同樣的數組,我們使用 map 來調用, 得到的是這樣的輸出:

[Optional("AA"), nil, Optional("BB"), Optional("CC")]

這就和原始數組一樣了。 這兩者的區別就是這樣。 map 函數值對元素進行變換操作。 但不會對數組的結構造成影響。 而 flatMap 會影響數組的結構。再進一步分析之前,我們暫且這樣理解。

flatMap 的這種機制,而已幫助我們方便的對數據進行驗證,比如我們有一組圖片文件名, 我們可以使用 flatMap 將無效的圖片過濾掉:

var imageNames = ["test.png", "aa.png", "icon.png"];
imageNames.flatMap{ UIImage(named: $0) }

那么 flatMap 是如何實現過濾掉 nil 值的呢? 我們還是來看一下源碼:

extension Sequence {
// ...
public func flatMap(
@noescape transform: (${GElement}) throws -> T?
) rethrows -> [T] {
var result: [T] = []
for element in self {
if let newElement = try transform(element) {
result.append(newElement)
}
}
return result
}
// ...
}

依然是遍歷所有元素,并應用 try transform(element) 閉包的調用, 但關鍵一點是,這里面用到了 if let 語句, 對那些只有解包成功的元素,才會添加到結果集中:

if let newElement = try transform(element) {
result.append(newElement)
}

這樣, 就實現了我們剛才看到的自動去掉 nil 值的效果了。

總結

以上是生活随笔為你收集整理的Swift中的map 和 flatMap 原理及用法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。