環(huán)境
- Xcode 12.2
前言
自從用上Swift后,就感覺Swift大法真是666,使用越多越覺得爽,下面就用一個場景再來吹一波??
功能很簡單,就是實現(xiàn)類似如下函數(shù):
func queryParameters(for url: URL?) -> [String: String] { }
相信這個功能大家都能很快搞定,但這里我想對這個函數(shù)再加上3個要求:
- 函數(shù)功能明確簡單,所以就不要寫return了
- 轉換步驟要一個一個來,盡量清晰,秒懂
- 盡量避免條件判斷
滿足這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大法呱呱叫??