Jak sprawdzić, czy mapa zawiera klucz w Go?

761

Wiem, że mogę iterować po mapie m,

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

i poszukaj klucza, ale czy istnieje bardziej skuteczny sposób testowania istnienia klucza na mapie?

Nie mogłem znaleźć odpowiedzi w specyfikacji języka .

grokus
źródło
2
Oto gdzie znaleźć odpowiedź w powiązanej specyfikacji: golang.org/ref/spec#Index_expressions
nobar

Odpowiedzi:

1471

Odpowiedź w jednym wierszu:

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

Wyjaśnienie:

ifinstrukcje w Go mogą zawierać zarówno warunek, jak i instrukcję inicjalizacji. W powyższym przykładzie wykorzystano oba:

  • inicjuje dwie zmienne - valotrzyma wartość „foo” z mapy lub „wartość zerową” (w tym przypadku pusty ciąg znaków) i okotrzyma wartość bool, która zostanie ustawiona, truejeśli „foo” faktycznie byłoby obecne na mapie

  • ocenia ok, co będzie, truejeśli „foo” było na mapie

Jeśli „foo” rzeczywiście jest obecne na mapie, treść ifinstrukcji zostanie wykonana i valbędzie lokalna w tym zakresie.

marketingowiec
źródło
2
Można to lepiej wyjaśnić, jak to działa (podobnie jak inny komentarz od peterSO)
Chmouel Boudjnah
6
@Kiril var val string = ""pozostanie taki sam, val, ok :=tworzy nową zmienną lokalną o tej samej nazwie, która jest widoczna tylko w tym bloku.
OneOfOne
1
świetna odpowiedź, czy powiedziałbyś, że złożonością tego jest O (1) ??
Mheni
1
@Mheni, wiem, że jestem tu trochę spóźniony, ale w tym pytaniu omówiono złożoność wyszukiwania. Przez większość czasu amortyzowana złożoność wynosi O (1), ale warto przeczytać odpowiedzi na to pytanie.
3ocen,
70
Taka myląca składnia w porównaniu do Pythona if key in dict.
Pranjal Mittal,
129

Oprócz specyfikacji języka programowania The Go , powinieneś przeczytać Effective Go . W części dotyczącej map mówią między innymi:

Próba pobrania wartości mapy za pomocą klucza, którego nie ma na mapie, zwróci wartość zerową dla typu pozycji na mapie. Na przykład, jeśli mapa zawiera liczby całkowite, wyszukiwanie nieistniejącego klucza zwróci 0. Zestaw można zaimplementować jako mapę o wartości typu bool. Ustaw wpis mapy na wartość true, aby wstawić wartość do zestawu, a następnie przetestuj ją przez proste indeksowanie.

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")
}

Czasami musisz odróżnić brakujący wpis od wartości zerowej. Czy istnieje wpis „UTC”, czy jest to 0, ponieważ w ogóle go nie ma na mapie? Możesz dyskryminować formę wielokrotnego przydziału.

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

Z oczywistych powodów nazywa się to idiomem „przecinek”. W tym przykładzie, jeśli występuje tz, sekundy zostaną odpowiednio ustawione i ok będzie prawdą; jeśli nie, sekundy zostaną ustawione na zero, a ok będzie fałszywe. Oto funkcja, która łączy go z ładnym raportem błędu:

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

Aby przetestować obecność na mapie bez obawy o rzeczywistą wartość, możesz użyć pustego identyfikatora (_) zamiast zwykłej zmiennej dla wartości.

_, present := timeZone[tz]
peterSO
źródło
57

Poszukałem na liście e-maili go-nuts i znalazłem rozwiązanie opublikowane przez Peter Froehlich w dniu 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")
        }
}

Lub, bardziej kompaktowo,

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

Uwaga: przy użyciu tej formy ifinstrukcji zmienne valuei oksą widoczne tylko wewnątrz ifwarunków.

grokus
źródło
20
Jeśli naprawdę interesuje Cię tylko to, czy klucz istnieje, i nie dbasz o wartość, możesz użyć _, ok := dict["baz"]; ok. _Część rzuca wartość dala zamiast tworzenia zmiennej tymczasowej.
Matthew Crumley,
26

Krótka odpowiedź

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

Przykład

Oto przykład na Go Playground .

Dłuższa odpowiedź

Według sekcji Effective Go w sekcji Mapy :

Próba pobrania wartości mapy za pomocą klucza, którego nie ma na mapie, zwróci wartość zerową dla typu pozycji na mapie. Na przykład, jeśli mapa zawiera liczby całkowite, wyszukiwanie nieistniejącego klucza zwróci 0.

Czasami musisz odróżnić brakujący wpis od wartości zerowej. Czy istnieje wpis „UTC”, czy jest to pusty ciąg, ponieważ w ogóle go nie ma na mapie? Możesz dyskryminować formę wielokrotnego przydziału.

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

Z oczywistych powodów nazywa się to idiomem „przecinek”. W tym przykładzie, jeśli występuje tz, sekundy zostaną odpowiednio ustawione i ok będzie prawdą; jeśli nie, sekundy zostaną ustawione na zero, a ok będzie fałszywe. Oto funkcja, która łączy go z ładnym raportem błędu:

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

Aby przetestować obecność na mapie bez obawy o rzeczywistą wartość, możesz użyć pustego identyfikatora (_) zamiast zwykłej zmiennej dla wartości.

_, present := timeZone[tz]
Matthew Rankin
źródło
13

Jak zauważono w innych odpowiedziach, ogólnym rozwiązaniem jest użycie wyrażenia indeksu w przypisaniu specjalnej formy:

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

To jest miłe i czyste. Ma jednak pewne ograniczenia: musi to być specjalne zadanie. Wyrażenie po prawej stronie musi być tylko wyrażeniem indeksu mapy, a lista wyrażeń po lewej stronie musi zawierać dokładnie 2 operandy, po pierwsze, do którego można przypisać typ wartości, a po drugie, do którego boolmożna przypisać wartość. Pierwsza wartość wyniku tego specjalnego formularza będzie wartością powiązaną z kluczem, a druga wartość pokaże, czy rzeczywiście istnieje wpis na mapie z danym kluczem (jeśli klucz istnieje na mapie). Lista wyrażeń po lewej stronie może również zawierać pusty identyfikator, jeśli jeden z wyników nie jest potrzebny.

Ważne jest, aby wiedzieć, że jeśli indeksowana wartość mapy zawiera nillub nie zawiera klucza, wyrażenie indeksu ocenia na wartość zerową typu wartości mapy. Na przykład:

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

Wypróbuj na placu zabaw Go .

Więc jeśli wiemy, że nie używamy wartości zerowej na naszej mapie, możemy to wykorzystać.

Na przykład, jeśli typem wartości jest stringi wiemy, że nigdy nie przechowujemy pozycji na mapie, gdzie wartość jest pustym ciągiem (zerowa wartość dla stringtypu), możemy również sprawdzić, czy klucz znajduje się na mapie, porównując niespecjalne forma (indeksu) wyrażenia indeksu do wartości zerowej:

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] != "")

Wyjście (wypróbuj na Go Playground ):

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

W praktyce istnieje wiele przypadków, w których nie przechowujemy wartości zerowej na mapie, więc można jej używać dość często. Na przykład interfejsy i typy funkcji mają zerową wartość nil, której często nie przechowujemy w mapach. Dlatego sprawdzenie, czy klucz znajduje się na mapie, można osiągnąć, porównując go nil.

Korzystanie z tej „techniki” ma jeszcze jedną zaletę: możesz sprawdzić istnienie wielu kluczy w kompaktowy sposób (nie możesz tego zrobić za pomocą specjalnego formularza „przecinek”). Więcej na ten temat: Sprawdź, czy klucz istnieje na wielu mapach w jednym stanie

Uzyskanie wartości zerowej typu wartości podczas indeksowania z nieistniejącym kluczem pozwala nam również boolwygodnie korzystać z map z wartościami jako zestawami . Na przykład:

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")
}

Wyprowadza (wypróbuj na Go Playground ):

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

Zobacz powiązane: Jak mogę utworzyć tablicę zawierającą unikalne ciągi?

icza
źródło
1
co jest Tw var v, ok T = a[x]środku nie okmusi być bool?
Kokizzu
2
@Kokizzu To jest ogólna forma deklaracji zmiennej. Na początku moglibyśmy myśleć, że działałoby (kompilowało) tylko, jeśli mapa byłaby typu map[bool]booli Tjest bool, ale działa również, jeśli mapa jest typu map[interface{}]booli Tjest interface{}; ponadto działa również z niestandardowymi typami posiadającymi booljako typ podstawowy, zobacz wszystkie na Go Playground . Ponieważ ten formularz jest prawidłowy, a wiele typów jest zastępowanych T, dlatego Tużywany jest ogólny . Typem okmoże być dowolny obiekt, do którego można przypisać typ bez typubool .
icza
7

lepszy sposób tutaj

if _, ok := dict["foo"]; ok {
    //do something here
}
Amazingandyyy
źródło
4
    var d map[string]string
    value, ok := d["key"]
    if ok {
        fmt.Println("Key Present ", value)
    } else {
        fmt.Println(" Key Not Present ")
    }
chandra
źródło
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)

Następnie uruchom mapy. Może coś istnieje? prawda nie istnieje? fałszywe

Lady_Exotel
źródło
Pozbywa się
Dzięki za wkład, ale myślę, że obecne odpowiedzi dobrze obejmują pytanie. Z tego, co tu mówisz, twoja odpowiedź bardziej pasowałaby do Najlepszego sposobu implementacji zestawu pytań typu Go .
tomasz
_, ok = m["somestring"]powinno być=_, ok := m["somestring"]
Elroy Jetson,
3

Jest wspomniany w „Wyrażeniach indeksu” .

Wyrażenie indeksu na mapie typu mapy [K] V używane w przypisaniu lub inicjacji formularza specjalnego

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

daje dodatkową nietypową wartość boolowską. Wartość ok jest prawdą, jeśli klucz x jest obecny na mapie, a fałsz w przeciwnym razie.

mroman
źródło
1

W tym celu można zastosować przypisanie dwóch wartości. Sprawdź mój przykładowy program poniżej

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.
źródło