Bagaimana cara memeriksa apakah peta berisi kunci di Go?

762

Saya tahu saya bisa mengulangi peta mdengan,

for k, v := range m { ... }

dan mencari kunci tetapi apakah ada cara yang lebih efisien untuk menguji keberadaan kunci di peta?

Saya tidak dapat menemukan jawabannya dalam spesifikasi bahasa .

grokus
sumber
2
Di sinilah tempat untuk menemukan jawabannya dalam spec yang terhubung: golang.org/ref/spec#Index_expressions
nobar

Jawaban:

1474

Satu jawaban baris:

if val, ok := dict["foo"]; ok {
    //do something here
}

Penjelasan:

ifpernyataan dalam Go dapat mencakup kondisi dan pernyataan inisialisasi. Contoh di atas menggunakan keduanya:

  • menginisialisasi dua variabel - valakan menerima nilai "foo" dari peta atau "nilai nol" (dalam hal ini string kosong) dan okakan menerima bool yang akan ditetapkan truejika "foo" benar-benar ada di peta

  • mengevaluasi ok, yang akan terjadi truejika "foo" ada di peta

Jika "foo" memang ada di peta, badan ifpernyataan akan dieksekusi dan valakan menjadi lokal untuk ruang lingkup itu.

pemasar
sumber
2
Ini mungkin bisa dijelaskan dengan lebih baik cara kerjanya (seperti komentar lain dari peterSO)
Chmouel Boudjnah
6
@ Kiril var val string = ""akan tetap sama, val, ok :=membuat variabel lokal baru dengan nama yang sama yang hanya dapat dilihat di blok itu.
OneOfOne
1
jawaban yang bagus, apakah Anda akan mengatakan kompleksitasnya adalah O (1) ??
Mheni
1
@Mheni, saya tahu saya agak terlambat di sini, tapi pertanyaan ini membahas kompleksitas pencarian. Sebagian besar waktu kompleksitas diamortisasi adalah O (1) tetapi ada baiknya membaca jawaban atas pertanyaan itu.
3ocene
70
Sintaks yang membingungkan dibandingkan dengan python if key in dict.
Pranjal Mittal
130

Selain Spesifikasi Bahasa Pemrograman Go , Anda harus membaca Efektif Go . Di bagian peta , kata mereka, antara lain:

Upaya mengambil nilai peta dengan kunci yang tidak ada di peta akan mengembalikan nilai nol untuk jenis entri di peta. Misalnya, jika peta berisi bilangan bulat, mencari kunci yang tidak ada akan mengembalikan 0. Satu set dapat diimplementasikan sebagai peta dengan tipe bool nilai. Setel entri peta menjadi true untuk meletakkan nilai dalam set, dan kemudian mengujinya dengan pengindeksan sederhana.

attended := map[string]bool{
    "Ann": true,
    "Joe": true,
    ...
}

if attended[person] { // will be false if person is not in the map
    fmt.Println(person, "was at the meeting")
}

Terkadang Anda perlu membedakan entri yang hilang dari nilai nol. Apakah ada entri untuk "UTC" atau 0 karena itu sama sekali tidak ada di peta? Anda dapat membeda-bedakan dengan bentuk penugasan ganda.

var seconds int
var ok bool
seconds, ok = timeZone[tz]

Untuk alasan yang jelas ini disebut idiom "koma ok". Dalam contoh ini, jika tz ada, detik akan diset dengan tepat dan ok akan benar; jika tidak, detik akan diatur ke nol dan ok akan salah. Berikut adalah fungsi yang menggabungkannya dengan laporan kesalahan yang bagus:

func offset(tz string) int {
    if seconds, ok := timeZone[tz]; ok {
        return seconds
    }
    log.Println("unknown time zone:", tz)
    return 0
}

Untuk menguji keberadaan di peta tanpa khawatir tentang nilai aktual, Anda dapat menggunakan pengidentifikasi kosong (_) sebagai pengganti variabel biasa untuk nilai tersebut.

_, present := timeZone[tz]
peterSO
sumber
57

Mencari di daftar email go-nuts dan menemukan solusi yang diposting oleh Peter Froehlich pada 11/15/2009.

package main

import "fmt"

func main() {
        dict := map[string]int {"foo" : 1, "bar" : 2}
        value, ok := dict["baz"]
        if ok {
                fmt.Println("value: ", value)
        } else {
                fmt.Println("key not found")
        }
}

Atau, lebih kompak,

if value, ok := dict["baz"]; ok {
    fmt.Println("value: ", value)
} else {
    fmt.Println("key not found")
}

Catatan, menggunakan bentuk ifpernyataan ini, variabel valuedan okhanya terlihat di dalam ifkondisi.

grokus
sumber
21
Jika Anda benar-benar hanya tertarik pada apakah kunci itu ada atau tidak, dan tidak peduli dengan nilainya, Anda dapat menggunakannya _, ok := dict["baz"]; ok. Bagian _membuang nilai alih-alih membuat variabel sementara.
Matthew Crumley
26

Jawaban singkat

_, exists := timeZone[tz]    // Just checks for key existence
val, exists := timeZone[tz]  // Checks for key existence and retrieves the value

Contoh

Berikut ini contoh di Go Playground .

Jawaban yang lebih panjang

Per bagian Peta dari Go Efektif :

Upaya mengambil nilai peta dengan kunci yang tidak ada di peta akan mengembalikan nilai nol untuk jenis entri di peta. Misalnya, jika peta berisi bilangan bulat, mencari kunci yang tidak ada akan mengembalikan 0.

Terkadang Anda perlu membedakan entri yang hilang dari nilai nol. Apakah ada entri untuk "UTC" atau apakah itu string kosong karena tidak ada di peta sama sekali? Anda dapat membeda-bedakan dengan bentuk penugasan ganda.

var seconds int
var ok bool
seconds, ok = timeZone[tz]

Untuk alasan yang jelas ini disebut idiom "koma ok". Dalam contoh ini, jika tz ada, detik akan diset dengan tepat dan ok akan benar; jika tidak, detik akan diatur ke nol dan ok akan salah. Berikut adalah fungsi yang menggabungkannya dengan laporan kesalahan yang bagus:

func offset(tz string) int {
    if seconds, ok := timeZone[tz]; ok {
        return seconds
    }
    log.Println("unknown time zone:", tz)
    return 0
}

Untuk menguji keberadaan di peta tanpa khawatir tentang nilai aktual, Anda dapat menggunakan pengidentifikasi kosong (_) sebagai pengganti variabel biasa untuk nilai tersebut.

_, present := timeZone[tz]
Matthew Rankin
sumber
13

Seperti dicatat oleh jawaban lain, solusi umum adalah dengan menggunakan ekspresi indeks dalam penugasan bentuk khusus:

v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
var v, ok T = a[x]

Ini bagus dan bersih. Meskipun memiliki beberapa batasan: itu harus berupa penugasan bentuk khusus. Ekspresi sisi kanan harus hanya ekspresi indeks peta, dan daftar ekspresi tangan kiri harus berisi tepat 2 operan, pertama yang jenis nilai dapat ditugaskan, dan yang kedua dimana boolnilai ditugaskan. Nilai pertama dari hasil bentuk khusus ini akan menjadi nilai yang terkait dengan kunci, dan nilai kedua akan memberi tahu apakah sebenarnya ada entri di peta dengan kunci yang diberikan (jika kunci ada di peta). Daftar ekspresi sisi kiri juga dapat berisi pengidentifikasi kosong jika salah satu hasil tidak diperlukan.

Penting untuk diketahui bahwa jika nilai peta yang diindeks adalah nilatau tidak mengandung kunci, ekspresi indeks dievaluasi ke nilai nol dari jenis nilai peta. Jadi misalnya:

m := map[int]string{}
s := m[1] // s will be the empty string ""
var m2 map[int]float64 // m2 is nil!
f := m2[2] // f will be 0.0

fmt.Printf("%q %f", s, f) // Prints: "" 0.000000

Cobalah di Go Playground .

Jadi jika kita tahu bahwa kita tidak menggunakan nilai nol di peta kita, kita bisa mengambil keuntungan dari ini.

Sebagai contoh jika jenis nilai adalah string, dan kita tahu kita tidak pernah menyimpan entri di peta di mana nilainya adalah string kosong (nilai nol untuk stringjenis), kita juga dapat menguji apakah kuncinya ada di peta dengan membandingkan non-khusus bentuk (hasil dari) ekspresi indeks ke nilai nol:

m := map[int]string{
    0: "zero",
    1: "one",
}

fmt.Printf("Key 0 exists: %t\nKey 1 exists: %t\nKey 2 exists: %t",
    m[0] != "", m[1] != "", m[2] != "")

Output (coba di Go Playground ):

Key 0 exists: true
Key 1 exists: true
Key 2 exists: false

Dalam praktiknya ada banyak kasus di mana kami tidak menyimpan nilai nol di peta, jadi ini bisa digunakan cukup sering. Misalnya antarmuka dan tipe fungsi memiliki nilai nol nil, yang sering tidak kami simpan di peta. Jadi menguji apakah kunci di peta dapat dicapai dengan membandingkannya nil.

Menggunakan "teknik" ini memiliki keuntungan lain juga: Anda dapat memeriksa keberadaan beberapa tombol dengan cara yang ringkas (Anda tidak dapat melakukannya dengan bentuk "koma ok" khusus). Lebih lanjut tentang ini: Periksa apakah ada kunci di beberapa peta dalam satu kondisi

Mendapatkan nilai nol dari tipe nilai saat pengindeksan dengan kunci yang tidak ada juga memungkinkan kita untuk menggunakan peta dengan boolnilai - nilai sesuai set . Sebagai contoh:

set := map[string]bool{
    "one": true,
    "two": true,
}

fmt.Println("Contains 'one':", set["one"])

if set["two"] {
    fmt.Println("'two' is in the set")
}
if !set["three"] {
    fmt.Println("'three' is not in the set")
}

Ini menghasilkan (coba di Go Playground ):

Contains 'one': true
'two' is in the set
'three' is not in the set

Lihat yang terkait: Bagaimana saya bisa membuat array yang berisi string unik?

icza
sumber
1
apa yang ada Tdi dalam var v, ok T = a[x]? bukankah okharus bool?
Kokizzu
2
@ Yokizzu Itulah bentuk umum dari deklarasi variabel. Pada awalnya kita mungkin berpikir itu hanya akan bekerja (kompilasi) jika peta akan jenis map[bool]booldan Tadalah bool, tetapi juga bekerja jika peta adalah tipe map[interface{}]booldan Tadalah interface{}; selain itu juga berfungsi dengan tipe khusus boolsebagai tipe yang mendasari, lihat semua di Go Playground . Jadi karena formulir itu valid dengan beberapa jenis yang diganti T, itu sebabnya yang umum Tdigunakan. Jenis okdapat berupa apa saja yang dapat diberikan pada siapa yang tidak diberi tagbool .
icza
7

cara yang lebih baik di sini

if _, ok := dict["foo"]; ok {
    //do something here
}
Amazingandyyy
sumber
4
    var d map[string]string
    value, ok := d["key"]
    if ok {
        fmt.Println("Key Present ", value)
    } else {
        fmt.Println(" Key Not Present ")
    }
chandra
sumber
3
    var empty struct{}
    var ok bool
    var m map[string]struct{}
    m = make(map[string]struct{})
    m["somestring"] = empty


    _, ok = m["somestring"]
    fmt.Println("somestring exists?", ok) 
    _, ok = m["not"]
    fmt.Println("not exists?", ok)

Lalu, jalankan menjalankan maps.go suatu tempat ada? benarkah tidak ada? Salah

Lady_Exotel
sumber
Menghilangkan kebutuhan akan int
Lady_Exotel
Terima kasih atas kontribusinya, tetapi saya pikir jawaban saat ini mencakup pertanyaan dengan baik. Dari apa yang Anda katakan di sini, jawaban Anda akan lebih cocok dengan cara terbaik menerapkan set in Go jenis pertanyaan.
tomasz
_, ok = m["somestring"] seharusnya =_, ok := m["somestring"]
Elroy Jetson
3

Disebutkan di bawah "Ekspresi indeks" .

Ekspresi indeks pada peta tipe peta [K] V yang digunakan dalam penugasan atau inisialisasi bentuk khusus

v, ok = a[x] 
v, ok := a[x] 
var v, ok = a[x]

menghasilkan nilai boolean untyped tambahan. Nilai ok benar jika kunci x ada di peta, dan salah jika tidak.

mroman
sumber
1

Penugasan dua nilai dapat digunakan untuk tujuan ini. Silakan periksa program sampel saya di bawah ini

package main

import (
    "fmt"
)

func main() {
    //creating a map with 3 key-value pairs
    sampleMap := map[string]int{"key1": 100, "key2": 500, "key3": 999}
    //A two value assignment can be used to check existence of a key.
    value, isKeyPresent := sampleMap["key2"]
    //isKeyPresent will be true if key present in sampleMap
    if isKeyPresent {
        //key exist
        fmt.Println("key present, value =  ", value)
    } else {
        //key does not exist
        fmt.Println("key does not exist")
    }
}
Fathah Rehman P
sumber