Menentukan apakah kamus Swift berisi kunci dan mendapatkan salah satu nilainya


260

Saat ini saya menggunakan potongan kode (canggung) berikut untuk menentukan apakah kamus Swift (kosong) berisi kunci yang diberikan dan untuk mendapatkan satu (setiap) nilai dari kamus yang sama.

Bagaimana seseorang dapat menempatkan ini dengan lebih elegan di 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
}

Anda dapat menggunakan indexForKeyjika Anda merasa lebih jelas dan lebih eksplisit; stackoverflow.com/a/29299943/294884
Fattie

Sangat sering yang Anda inginkan pada dasarnya adalah: cityName:String = dict["city"] ?? "" Di ?? ""sini pada dasarnya berarti "jika tidak ada kunci seperti itu, kembalikan yang kosong".
Fattie

Jawaban:


481

Anda tidak perlu setiap kode khusus untuk melakukan hal ini, karena itu adalah apa kamus sudah tidak. Ketika Anda mengambil dict[key]Anda tahu apakah kamus berisi kunci, karena Opsional yang Anda dapatkan tidak nil(dan itu berisi nilai).

Jadi, jika Anda hanya ingin menjawab pertanyaan apakah kamus berisi kunci, tanyakan:

let keyExists = dict[key] != nil

Jika Anda menginginkan nilainya dan Anda tahu kamus berisi kunci, katakan:

let val = dict[key]!

Tetapi jika, seperti biasanya terjadi, Anda tidak tahu itu berisi kunci - Anda ingin mengambil dan menggunakannya, tetapi hanya jika ada - maka gunakan sesuatu seperti if let:

if let val = dict[key] {
    // now val is not nil and the Optional has been unwrapped, so use it
}

6
Saya tidak mengikuti. Saya baru saja mendapatkan nilai (dua kali). Apa lagi yang kamu inginkan?
matt

Tidak ada yang namanya "nilai pertama"; kamus tidak berurutan. Jika Anda hanya menginginkan nilai, tanpa kunci, katakan dict.values.
matt

1
Hati-hati karena dict.valuesburam. Anda dapat bersepeda melalui itu tapi itu saja. (Oke, bukan hanya itu, tapi buat aku senang.) Jika kamu ingin itu diubah menjadi Array, ambil dict.values.array.
matt

1
@bubakazouba Coba ini: let d = ["hey":"ho"]; let s = d["hey"]; print(s)Ini Opsional, sama seperti dulu.
matt

1
@ Lintas Itu adalah pertanyaan yang sangat mendalam tetapi karena itu silakan tanyakan secara terpisah.
matt

68

Mengapa tidak memeriksa saja dict.keys.contains(key)? Memeriksa dict[key] != niltidak akan berfungsi dalam kasus di mana nilainya nol. Seperti halnya dengan kamus [String: String?]misalnya.


2
Atau alih-alih hanya menulis if let val = dict[key]Anda dapat menggunakan if let val = dict[key] as? Stringdalam hal ini.
Okhan Okbay

.keys.contains tidak membantu membedakan apakah kunci itu ada, atau jika nihil. Mungkin di versi cepat sebelumnya? Tidak bekerja untuk saya dengan cepat 4.
Rowan Gontier

8
Ini berpotensi sangat boros, karena (1) Anda memanggil dict.keysyang membuat array dengan semua kunci, kemudian (2) memeriksa setiap kunci secara berurutan sampai Anda menemukan satu (atau tidak ada!). Saya sadar Anda sedang berusaha menangani kasus a [String: String?], tetapi akan sangat berhati-hati dengan solusi itu ...
charles

3
Seperti @charles sebutkan, ini akan menghilangkan manfaat pencarian cepat yang menggunakan kamus, jadi saya akan menyarankan yang ini, tapi masih jelas merupakan opsi yang valid.
businesscasual

4
Anda tidak boleh menggunakan metode ini, karena memiliki kompleksitas O (n) alih-alih teoretis (tergantung pada implementasi fungsi hash) O (1) dari akses kamus langsung.
Krypt

45

Jawaban yang diterima let keyExists = dict[key] != niltidak akan berfungsi jika Kamus berisi kunci tetapi memiliki nilai nihil.

Jika Anda ingin memastikan Kamus tidak mengandung kunci sama sekali gunakan ini (diuji dalam Swift 4).

if dict.keys.contains(key) {
  // contains key
} else { 
  // does not contain key
}

3
Ini sama dengan jawaban Han.
Declan McKenna

1
Memeriksa == niltidak berfungsi, meskipun kamus memiliki nilai opsional. Ini karena hasil pencarian dibungkus sebagai Optional. Di sisi lain, untuk memeriksa apakah nilai tampak benar nil- benar daripada tidak ada, Anda dapat menggunakannya == .some(nil).
jedwidz

1
Saya akan menambahkan efisiensi menggunakan metode ini dibandingkan dengan pencarian O (1) dari metode lain. Saya percaya ini O (n) lookup
Alberto Vega

1
@ AlbertoVega-MSFT Ini O(1). Lihat juga: github.com/apple/swift-evolution/blob/master/proposals/… Apa yang membuat Anda berpikir itu O(n)?
Alexander - Reinstate Monica

1
Dokumentasi untuk Dictionary.Keys.contains(...)fungsi di sini mengatakan memiliki O(n)kompleksitas, di mana npanjang urutannya.
Adil Hussain

5

Sepertinya Anda mendapatkan apa yang Anda butuhkan dari @matt, tetapi jika Anda menginginkan cara cepat untuk mendapatkan nilai untuk kunci, atau hanya nilai pertama jika kunci itu tidak ada:

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”}

Catatan, tidak ada jaminan apa yang akan Anda dapatkan sebagai nilai pertama, itu hanya terjadi dalam kasus ini untuk mengembalikan "merah".


1
penggunaan yang baik dari ??operator mengambil solusi kenyamanan saya, tetapi sebagai perpanjangan nilai default itu dapat menghadirkan ketidakamanan data atau perilaku tak terduga. Kedengarannya seperti sesuatu subkelas konkret yang Dictionaryharus dipekerjakan. Seberapa sering Anda memerlukan nilai pertama jika nilpengembalian tidak termasuk fungsi khusus situasi?
NoodleOfDeath


0

Solusi saya untuk implementasi cache yang menyimpan NSAttributedString opsional:

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

Inilah yang berfungsi untuk saya di Swift 3

let _ = (dict[key].map { $0 as? String } ?? "")
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.