Как использовать необязательные цепочки при поиске в словаре в Swift?
Я хочу безопасно искать значения в быстром словаре, используя if и убедиться, что это типобезопасный, поскольку я все глубже и глубже в словаре. Словарь содержит словари, которые содержат NSArray, который содержит больше словаря.
С первой попытки мой код выглядит так:
if let kkbox = ticket["KKBOX"] as? Dictionary<String, AnyObject> {
if let kkboxDlUrlDict = kkbox["kkbox_dl_url_list"] as? Dictionary<String, AnyObject> {
if let kkboxDlUrlArray = kkboxDlUrlDict["kkbox_dl_url"] as? NSArray {
for dict in kkboxDlUrlArray {
if let name = dict["name"] as? String {
if name == mediaType.rawValue {
urlStr = dict["url"] as String
}
}
}
} else { return nil }
} else { return nil }
} else { return nil }
Как мне сократить его, возможно, до одной или двух строк?
Я понял, что могу связать это, если это 2 слоя. Это работает:
if let kkboxDlUrlArray = ticket["KKBOX"]?["kkbox_dl_url_list"] as? NSArray {
}
Но любая цепочка длиннее, не скомпилируется.
Есть ли способ пролистать словарь более одного раза?
Спасибо
3 ответа
Похоже, Swift 1.2 добавил эту функцию.
"Более мощное опциональное развертывание с помощью if let - Конструкция if let теперь может разворачивать несколько дополнительных опций одновременно, а также включать промежуточные логические условия. Это позволяет вам выражать поток условного управления без ненужного вложения".
В качестве альтернативы, вы можете использовать это расширение, которое имитирует оригинальный метод valueForKeyPath, который раньше существовал в NSDictionary, но был исключен по любой причине:
Swift 4.1
extension Dictionary where Key: ExpressibleByStringLiteral {
func valueFor(keyPath: String) -> Any? {
var keys = keyPath.components(separatedBy: ".")
var val : Any = self
while keys.count > 0 {
if let key = keys[0] as? Key {
keys.remove(at: 0)
if let dic = val as? Dictionary<Key, Value> {
if let leaf = dic[key] {
val = leaf
} else {
return nil
}
} else {
return nil
}
} else {
return nil
}
}
return val
}
Ваш код будет затем читать:
if let array = ticket.valueFor("KKBOX.kkbox_dl_url_list.kkbox_dl_url") as? [] {
// do something with array
}
Вы можете цепляться, но с правильным понижением на каждом шаге:
if let kkboxDlUrlArray = ((((ticket["KKBOX"] as? Dictionary<String, AnyObject>)?["kkbox_dl_url_list"]) as? Dictionary<String, AnyObject>)?["kkbox_dl_url"]) as? NSArray {
for dict in kkboxDlUrlArray {
println(dict)
}
}
Это выглядит не очень хорошо - это одна строка, но не читаемая.
Лично, не используя какой-либо причудливый функциональный способ сделать это, я бы сделал цепочку более явной с помощью только одного необязательного связывания:
let kkbox = ticket["KKBOX"] as? Dictionary<String, AnyObject>
let kkboxDlUrlDict = kkbox?["kkbox_dl_url_list"] as? Dictionary<String, AnyObject>
if let kkboxDlUrlArray = kkboxDlUrlDict?["kkbox_dl_url"] as? NSArray {
for dict in kkboxDlUrlArray {
println(dict)
}
}
На мой взгляд, намного легче читать, и без лишних отступов