Визначення, чи словник 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
}

Ви можете використовувати, indexForKeyякщо вважаєте, що це чіткіше і чіткіше; stackoverflow.com/a/29299943/294884
Fattie

Дуже часто те , що ви хочете , в основному: тут означає , що в принципі «якщо немає такого ключа, повертає порожній». cityName:String = dict["city"] ?? ""?? ""
Fattie

Відповіді:


481

Вам не потрібно ніякого спеціального коду , щоб зробити це, тому що це те , що словник вже робить. Коли ви dict[key]отримуєте інформацію, ви знаєте, чи містить словник ключ, тому що необов’язкове повернення не є 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 не допомагає розрізнити, чи існує ключ, чи значення є нульовим. Можливо, у попередніх швидких версіях? Чи не працює для мене в стрижа 4.
Роуен Гонтьер

8
Це потенційно дуже марно, оскільки (1) ви викликаєте, dict.keysякий створює масив з усіма ключами, а потім (2) перевіряє кожну клавішу, щоб не знайти її (або жодної!). Я усвідомлюю, що ви намагаєтеся вирішити випадок [String: String?], але будете дуже обережні з цим рішенням ...
Чарлз

3
Як згадувалося @charles, це дозволить усунути швидку перевагу швидкого пошуку, що забезпечує використання словника, тому я б радив проти цього, але все-таки, безумовно, дійсний варіант.
ділова справа

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працює, навіть якщо словник має необов'язкові значення. Це відбувається тому, що результат пошуку загортається як an Optional. З іншого боку nil, ви можете скористатися , щоб перевірити, чи справді шукане значення замість цього відсутнє, можна скористатися == .some(nil).
jedwidz

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


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 } ?? "")
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.