Wie überprüfe ich, ob eine Karte einen Schlüssel in Go enthält?

761

Ich weiß, dass ich über eine Karte iterieren kann, mindem ich

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

und nach einem Schlüssel suchen, aber gibt es eine effizientere Möglichkeit, die Existenz eines Schlüssels in einer Karte zu testen?

Ich konnte die Antwort in der Sprachspezifikation nicht finden .

Grokus
quelle
2
Hier finden Sie die Antwort in der verlinkten Spezifikation: golang.org/ref/spec#Index_expressions
nobar

Antworten:

1473

Einzeilige Antwort:

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

Erläuterung:

ifAnweisungen in Go können sowohl eine Bedingungs- als auch eine Initialisierungsanweisung enthalten. Im obigen Beispiel werden beide verwendet:

  • Initialisiert zwei Variablen - valerhält entweder den Wert "foo" von der Karte oder einen "Nullwert" (in diesem Fall die leere Zeichenfolge) und okerhält einen Bool, der auf gesetzt wird, truewenn "foo" tatsächlich in der Karte vorhanden war

  • wertet aus ok, was sein wird, truewenn "foo" in der Karte war

Wenn "foo" tatsächlich in der Karte vorhanden ist, wird der Hauptteil der ifAnweisung ausgeführt und valist lokal für diesen Bereich.

Vermarkter
quelle
2
Dies kann besser erklärt werden, wie es funktioniert (wie der andere Kommentar von PeterSO)
Chmouel Boudjnah
6
@Kiril var val string = ""bleibt gleich, val, ok :=erstellt eine neue lokale Variable mit demselben Namen, die nur in diesem Block sichtbar ist.
OneOfOne
1
tolle Antwort, würden Sie sagen, die Komplexität ist O (1)?
Mheni
1
@Mheni, ich weiß, dass ich hier etwas spät dran bin, aber diese Frage beschreibt die Komplexität der Suche in go. Meistens ist die amortisierte Komplexität O (1), aber es lohnt sich, die Antworten auf diese Frage zu lesen.
3ozän
70
Eine so verwirrende Syntax im Vergleich zu Python if key in dict.
Pranjal Mittal
130

Zusätzlich zur Spezifikation der Go-Programmiersprache sollten Sie Effective Go lesen . Im Abschnitt über Karten heißt es unter anderem:

Ein Versuch, einen Kartenwert mit einem Schlüssel abzurufen, der nicht in der Karte vorhanden ist, gibt den Nullwert für den Typ der Einträge in der Karte zurück. Wenn die Map beispielsweise Ganzzahlen enthält, gibt das Nachschlagen eines nicht vorhandenen Schlüssels 0 zurück. Eine Menge kann als Map mit dem Werttyp bool implementiert werden. Setzen Sie den Karteneintrag auf true, um den Wert in den Satz aufzunehmen, und testen Sie ihn dann durch einfaches Indizieren.

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

Manchmal müssen Sie einen fehlenden Eintrag von einem Nullwert unterscheiden. Gibt es einen Eintrag für "UTC" oder ist das 0, weil es überhaupt nicht in der Karte ist? Sie können mit einer Form der Mehrfachzuweisung unterscheiden.

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

Aus offensichtlichen Gründen wird dies als "Komma ok" -Sprache bezeichnet. Wenn in diesem Beispiel tz vorhanden ist, werden die Sekunden entsprechend eingestellt und ok ist wahr. Wenn nicht, werden die Sekunden auf Null gesetzt und ok ist falsch. Hier ist eine Funktion, die es mit einem schönen Fehlerbericht zusammenfügt:

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

Um das Vorhandensein in der Karte zu testen, ohne sich um den tatsächlichen Wert zu kümmern, können Sie anstelle der üblichen Variablen für den Wert den leeren Bezeichner (_) verwenden.

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

Durchsuchte die verrückte E-Mail-Liste und fand eine Lösung, die von Peter Froehlich am 15.11.2009 veröffentlicht wurde.

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

Oder kompakter:

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

Beachten Sie, dass bei Verwendung dieser Form der ifAnweisung die Variablen valueund oknur innerhalb der ifBedingungen sichtbar sind .

Grokus
quelle
21
Wenn Sie wirklich nur daran interessiert sind, ob der Schlüssel vorhanden ist oder nicht, und sich nicht für den Wert interessieren, können Sie ihn verwenden _, ok := dict["baz"]; ok. Der _Teil wirft den Wert weg, anstatt eine temporäre Variable zu erstellen.
Matthew Crumley
26

Kurze Antwort

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

Beispiel

Hier ist ein Beispiel auf dem Go Playground .

Längere Antwort

Im Abschnitt " Karten " von Effective Go :

Ein Versuch, einen Kartenwert mit einem Schlüssel abzurufen, der nicht in der Karte vorhanden ist, gibt den Nullwert für den Typ der Einträge in der Karte zurück. Wenn die Map beispielsweise Ganzzahlen enthält, gibt das Nachschlagen eines nicht vorhandenen Schlüssels 0 zurück.

Manchmal müssen Sie einen fehlenden Eintrag von einem Nullwert unterscheiden. Gibt es einen Eintrag für "UTC" oder ist das die leere Zeichenfolge, weil sie überhaupt nicht in der Karte enthalten ist? Sie können mit einer Form der Mehrfachzuweisung unterscheiden.

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

Aus offensichtlichen Gründen wird dies als "Komma ok" -Sprache bezeichnet. Wenn in diesem Beispiel tz vorhanden ist, werden die Sekunden entsprechend eingestellt und ok ist wahr. Wenn nicht, werden die Sekunden auf Null gesetzt und ok ist falsch. Hier ist eine Funktion, die es mit einem schönen Fehlerbericht zusammenfügt:

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

Um das Vorhandensein in der Karte zu testen, ohne sich um den tatsächlichen Wert zu kümmern, können Sie anstelle der üblichen Variablen für den Wert den leeren Bezeichner (_) verwenden.

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

Wie in anderen Antworten angegeben, besteht die allgemeine Lösung darin, einen Indexausdruck in einer Zuweisung der speziellen Form zu verwenden:

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

Das ist schön und sauber. Es gibt jedoch einige Einschränkungen: Es muss sich um eine Zuweisung einer speziellen Form handeln. Der Ausdruck auf der rechten Seite darf nur der Kartenindexausdruck sein, und die Liste der Ausdrücke auf der linken Seite muss genau zwei Operanden enthalten, denen zum einen der Werttyp und zum anderen ein boolWert zuweisbar ist. Der erste Wert des Ergebnisses dieses speziellen Formulars ist der dem Schlüssel zugeordnete Wert, und der zweite Wert gibt an, ob tatsächlich ein Eintrag in der Karte mit dem angegebenen Schlüssel vorhanden ist (wenn der Schlüssel in der Karte vorhanden ist). Die Ausdrucksliste auf der linken Seite kann auch die leere Kennung enthalten wenn eines der Ergebnisse nicht benötigt wird.

Es ist wichtig zu wissen, dass der Indexausdruck nilden Nullwert des Werttyps der Karte ergibt, wenn der indizierte Kartenwert den Schlüssel enthält oder nicht . Also zum Beispiel:

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

Probieren Sie es auf dem Go Playground aus .

Wenn wir also wissen, dass wir den Nullwert in unserer Karte nicht verwenden, können wir dies nutzen.

Wenn der Werttyp beispielsweise ist stringund wir wissen, dass wir niemals Einträge in der Karte speichern, in denen der Wert die leere Zeichenfolge ist (Nullwert für den stringTyp), können wir auch testen, ob sich der Schlüssel in der Karte befindet, indem wir das Nicht-Spezial vergleichen Form des (Ergebnisses des) Indexausdrucks auf den Nullwert:

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

Ausgabe (versuchen Sie es auf dem Go-Spielplatz ):

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

In der Praxis gibt es viele Fälle, in denen wir den Nullwert nicht in der Karte speichern, sodass dieser häufig verwendet werden kann. Beispielsweise haben Schnittstellen und Funktionstypen einen Nullwert nil, den wir häufig nicht in Karten speichern. Das Testen, ob sich ein Schlüssel in der Karte befindet, kann also durch Vergleichen mit erreicht werden nil.

Die Verwendung dieser "Technik" hat noch einen weiteren Vorteil: Sie können das Vorhandensein mehrerer Schlüssel auf kompakte Weise überprüfen (dies ist mit dem speziellen Formular "Komma OK" nicht möglich). Mehr dazu: Überprüfen Sie, ob der Schlüssel in mehreren Karten unter einer Bedingung vorhanden ist

Wenn Sie beim Indizieren mit einem nicht vorhandenen Schlüssel den Nullwert des Wertetyps erhalten, können Sie auch Karten mit boolWerten bequem als Mengen verwenden . Zum Beispiel:

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

Es gibt aus (versuchen Sie es auf dem Go Playground ):

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

Siehe verwandte: Wie kann ich ein Array erstellen, das eindeutige Zeichenfolgen enthält?

icza
quelle
1
Was ist Tin var v, ok T = a[x]? ist nicht oksein Bool muss?
Kokizzu
2
@Kokizzu Das ist die allgemeine Form der Variablendeklaration. Zuerst könnten wir denken, dass es nur funktionieren würde (kompilieren), wenn die Karte vom Typ wäre map[bool]boolund Tist bool, aber es funktioniert auch, wenn die Karte vom Typ ist map[interface{}]boolund Tist interface{}; Darüber hinaus funktioniert es auch mit benutzerdefinierten Typen, deren boolzugrunde liegender Typ alle auf dem Go Playground anzeigen . Da dieses Formular mit mehreren ersetzten Typen gültig ist, wird Tdas Allgemeine Tverwendet. Typ von okkann alles sein, dem ein untypisierterbool Typ zugewiesen werden kann.
icza
7

besserer Weg hierher

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

Dann gehen Sie Karten ausführen. Gibt es etwas, das existiert? wahr existiert nicht? falsch

Lady_Exotel
quelle
Beseitigt die Notwendigkeit für int
Lady_Exotel
Vielen Dank für den Beitrag, aber ich denke, die aktuellen Antworten decken die Frage gut ab. Nach dem, was Sie hier sagen, passt Ihre Antwort besser zu einer Frage, die am besten in der Art des Go- Fragetyps implementiert ist .
Thomasz
_, ok = m["somestring"]sollte sein=_, ok := m["somestring"]
Elroy Jetson
3

Es wird unter "Indexausdrücke" erwähnt .

Ein Indexausdruck auf einer Karte vom Typ map [K] V, der bei einer Zuweisung oder Initialisierung des speziellen Formulars verwendet wird

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

ergibt einen zusätzlichen untypisierten Booleschen Wert. Der Wert von ok ist true, wenn der Schlüssel x in der Karte vorhanden ist, andernfalls false.

mroman
quelle
1

Zu diesem Zweck kann eine Zwei-Wert-Zuordnung verwendet werden. Bitte überprüfen Sie mein Beispielprogramm unten

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.
quelle