一個map家族常用場景吹一波Swift

環(huán)境

  • Xcode 12.2

前言

自從用上Swift后,就感覺Swift大法真是666,使用越多越覺得爽,下面就用一個場景再來吹一波??

功能很簡單,就是實現(xiàn)類似如下函數(shù):

func queryParameters(for url: URL?) -> [String: String] { }

相信這個功能大家都能很快搞定,但這里我想對這個函數(shù)再加上3個要求:

  1. 函數(shù)功能明確簡單,所以就不要寫return了
  2. 轉換步驟要一個一個來,盡量清晰,秒懂
  3. 盡量避免條件判斷

滿足這3個要求后,最后寫出來的代碼應該就能比較Swifty了吧??

先放個最后自己寫好的,感興趣的話可以再看看后面的過程:

func queryParameters(for url: URL?) -> [String: String] {
    (url?.absoluteString)
        .flatMap { URLComponents(string: $0) }?
        .queryItems?
        .compactMap { item in
            item.value?.removingPercentEncoding.flatMap { (item.name, $0) }
        }
        .toDictionary() ?? [:]
}

思路

首先,想省去return,但中間又會有各種轉換,那就用鏈式調用方式唄。

然后我這里想使用URLComponents的queryItems來省去自己拆分參數(shù)的煩惱。。

URL -> URLComponents

使用URLComponents(string:),而不是URLComponents(url:, resolvingAgainstBaseURL:)可以多遇到一個問題。。

要對可選類型做轉換,flatMap出場,不過剛開始就遇到個問題??,第一次代碼:

url?.absoluteString.flatMap { URLComponents(string: $0) }

發(fā)現(xiàn)報錯:

Cannot convert value of type 'String.Element' (aka 'Character') to expected argument type 'String'

flatMap里面的$0是Character,而不是String,因為String實現(xiàn)了Collection協(xié)議(這個在Swift大版本中改過去又改過來)。

那試試加個?號:

url?.absoluteString?.flatMap { URLComponents(string: $0) }

結果還是報錯,想了下,最后加括號搞定:

(url?.absoluteString)
    .flatMap { URLComponents(string: $0) }?
    .queryItems?

上面說的多遇到一個問題就是如此,需要引入括號來避免歧義,但其實這里最優(yōu)的寫法應該如下:

url.flatMap { URLComponents(url: $0, resolvingAgainstBaseURL: true) }?
        .queryItems?

ok,拿到queryItems了,不過是[URLQueryItem]類型,下面繼續(xù)。

[URLQueryItem] -> [(String,String)]

[URLQueryItem]不方便直接轉換[String: String],需要先轉換成 [(String,String)] ,再使用Dictionary(uniqueKeysWithValues:)來完成目標。

Map

那還不簡單么,集合轉換,一個map不就搞定了:

url.flatMap { URLComponents(url: $0, resolvingAgainstBaseURL: true) }?
        .queryItems?
        .map { ($0.name, $0.value) }

這樣語法是沒錯,但有個問題,URLQueryItem的value是String?,而不是String,

所以這個轉換結果是 [(String,String?)] ,而不是 [(String,String)] ,不符合要求。

那就只有過濾掉value為nil的情況了,有請map大哥compactMap出場。

compactMap這貨也是中間殺出來多,最開始只有map和flatMap。

compactMap

要過濾掉nil,那是不是得先判斷下?沒有值就返回nil?

其實不用,flatMap可以優(yōu)雅的實現(xiàn)這個功能??,順便把value decode了:

url.flatMap { URLComponents(url: $0, resolvingAgainstBaseURL: true) }?
      .queryItems?
      .compactMap { item in
          item.value?.removingPercentEncoding.flatMap { (item.name, $0) }
      }

OK,這里我們順利轉換成[(String,String)] 了,還有最后一步。

[(String,String)] -> [String: String]

最后發(fā)現(xiàn)好像沒法直接鏈式調用Dictionary(uniqueKeysWithValues: ),雜辦呢?

extension出場,加上where可以限制元素類型,真是666??

extension Array where Element == (String, String) {
    public func toDictionary() -> [String: String] {
        Dictionary(uniqueKeysWithValues: self)
    }
}

可選鏈到最后也是可選的,所以加個默認空值,搞定?。?!

func queryParameters(for url: URL?) -> [String: String] {
    url.flatMap { URLComponents(url: $0, resolvingAgainstBaseURL: true) }?
        .queryItems?
        .compactMap { item in
            item.value?.removingPercentEncoding.flatMap { (item.name, $0) }
        }
        .toDictionary() ?? [:]
}

總結

Swift大法呱呱叫??

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容