Определение, содержит ли словарь Swift ключ, и получение любого из его значений

260

В настоящее время я использую следующие (неуклюжие) фрагменты кода для определения, содержит ли (непустой) словарь Swift заданный ключ, и для получения одного (любого) значения из того же словаря.

Как можно выразить это более элегантно в Swift?

// excerpt from method that determines if dict contains key
if let _ = dict[key] {
    return true
}
else {
    return false
}

// excerpt from method that obtains first value from dict
for (_, value) in dict {
    return value
}
Drux
источник
Вы можете использовать, indexForKeyесли чувствуете, что это яснее и понятнее; stackoverflow.com/a/29299943/294884
Толстяк
Очень часто то , что вы хотите , в основном: здесь означает , что в принципе « если нет такого ключа, возвращает пустой». cityName:String = dict["city"] ?? ""?? ""
Толстяк

Ответы:

481

Вам не нужно никакого специального кода , чтобы сделать это, потому что это то , что словарь уже делает. Когда вы выбираете, dict[key]вы знаете , содержит ли словарь ключ, потому что возвращаемое значение Optional отсутствует nil(и оно содержит значение).

Итак, если вы просто хотите ответить на вопрос, содержит ли словарь ключ, спросите:

let keyExists = dict[key] != nil

Если вам нужно значение и вы знаете, что словарь содержит ключ, скажите:

let val = dict[key]!

Но если, как обычно, вы не знаете, что он содержит ключ - вы хотите получить его и использовать, но только если он существует - тогда используйте что-то вроде if let:

if let val = dict[key] {
    // now val is not nil and the Optional has been unwrapped, so use it
}
матовый
источник
6
Я не следую Я только что получил значение (дважды). Что вы еще хотите?
Мэтт
Нет такой вещи как «первое значение»; словарь неупорядочен. Если вы просто хотите значения, без ключей, скажем dict.values.
Мэтт
1
Будьте осторожны, потому что dict.valuesнепрозрачный. Вы можете ездить на велосипеде, но это все. (Ладно, это еще не все, но приколите меня.) Если вы хотите, чтобы он был преобразован в массив, возьмите dict.values.array.
Мэтт
1
@bubakazouba Попробуйте это: let d = ["hey":"ho"]; let s = d["hey"]; print(s)это необязательно, как всегда.
Мэтт
1
@Kross Это очень глубокий вопрос, но, пожалуйста, задавайте его отдельно.
Мэтт
68

Почему бы просто не проверить dict.keys.contains(key)? Проверка dict[key] != nilне будет работать в случаях, когда значение равно нулю. Как со словарем, [String: String?]например.

Ханс Терье Бакке
источник
2
Или вместо того, чтобы просто писать, if let val = dict[key]вы можете использовать if let val = dict[key] as? Stringв этом случае.
Охан Окбай
.keys.contains не помогает определить, существует ли ключ или значение равно нулю. Может быть, в более ранних быстрых версиях? Не работает для меня в Swift 4.
Rowan Gontier
8
Это потенциально очень расточительно, потому что (1) вы вызываете, dict.keysкоторый создает массив со всеми ключами, затем (2) проверяет каждый ключ по порядку, пока не найдете один (или ни один!). Я понимаю, что вы пытаетесь разобраться с делом [String: String?], но очень осторожно с этим решением ...
Чарльз
3
Как упоминалось в @charles, это исключило бы преимущество быстрого поиска, которое дает использование словаря, поэтому я бы посоветовал против этого, но все же определенно допустимый вариант.
businesscasual
4
Вы никогда не должны использовать этот метод, он имеет сложность O (n) вместо теоретической (зависит от реализации хэш-функции) O (1) прямого доступа к словарю.
Крипт
45

Принятый ответ let keyExists = dict[key] != nilне будет работать, если словарь содержит ключ, но имеет значение ноль.

Если вы хотите быть уверенным, что в словаре вообще нет ключа, используйте его (протестировано в Swift 4).

if dict.keys.contains(key) {
  // contains key
} else { 
  // does not contain key
}
Обломок айсберга
источник
3
Это так же, как ответ Хана.
Деклан МакКенна
1
Проверка == nilработает, даже если в словаре есть необязательные значения. Это потому, что результат поиска обернут как Optional. С другой стороны, чтобы проверить, является ли искомое значение действительно, nilа не отсутствует, вы можете использовать == .some(nil).
Джедвидз
1
Я бы добавил эффективность использования этого метода по сравнению с поиском O (1) других методов. Я считаю, что это O (N) поиск
Альберто Вега
1
@ AlbertoVega-MSFT Это O(1). Смотрите также: github.com/apple/swift-evolution/blob/master/proposals/… Что заставило вас так думать O(n)?
Александр - Восстановить Монику
1
Документация для Dictionary.Keys.contains(...)функции здесь говорит, что она имеет O(n)сложность, где nдлина последовательности.
Адиль Хуссейн
5

Похоже, вы получили то, что вам нужно от @matt, но если вы хотите быстрый способ получить значение для ключа или просто первое значение, если этот ключ не существует:

extension Dictionary {
    func keyedOrFirstValue(key: Key) -> Value? {
        // if key not found, replace the nil with 
        // the first element of the values collection
        return self[key] ?? first(self.values)
        // note, this is still an optional (because the
        // dictionary could be empty)
    }
}

let d = ["one":"red", "two":"blue"]

d.keyedOrFirstValue("one")  // {Some "red"}
d.keyedOrFirstValue("two")  // {Some "blue"}
d.keyedOrFirstValue("three")  // {Some "red”}

Обратите внимание, нет никаких гарантий, что вы на самом деле получите в качестве первого значения, просто в этом случае возвращается «красный».

Скорость полёта
источник
1
При правильном использовании ??оператора мое удобное решение было принято у меня, но в качестве расширения значение по умолчанию могло представлять отсутствие безопасности данных или неожиданное поведение. Это звучит как что-то, что Dictionaryдолжен использовать конкретный подкласс . Как часто вам нужно первое значение в случае nilвозврата, исключая специфические для конкретной ситуации функции?
NoodleOfDeath
1
if dictionayTemp["quantity"] != nil
    {

  //write your code
    }
Пареш Хирпара
источник
0

Мое решение для реализации кэша, который хранит необязательный NSAttributedString:

public static var attributedMessageTextCache    = [String: NSAttributedString?]()

    if attributedMessageTextCache.index(forKey: "key") != nil
    {
        if let attributedMessageText = TextChatCache.attributedMessageTextCache["key"]
        {
            return attributedMessageText
        }
        return nil
    }

    TextChatCache.attributedMessageTextCache["key"] = .some(.none)
    return nil
Риши
источник
0

Вот что у меня работает на Swift 3

let _ = (dict[key].map { $0 as? String } ?? "")
ΩlostA
источник