Как использовать необязательные цепочки при поиске в словаре в 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 теперь может разворачивать несколько дополнительных опций одновременно, а также включать промежуточные логические условия. Это позволяет вам выражать поток условного управления без ненужного вложения".

https://developer.apple.com/swift/blog/

В качестве альтернативы, вы можете использовать это расширение, которое имитирует оригинальный метод 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)
    }
}

На мой взгляд, намного легче читать, и без лишних отступов

Другие вопросы по тегам