Comment vérifier si une carte contient une clé dans Go?

763

Je sais que je peux parcourir une carte men

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

et recherchez une clé, mais existe-t-il un moyen plus efficace de tester l'existence d'une clé dans une carte?

Je n'ai pas pu trouver la réponse dans la spécification de langue .

grokus
la source
2
Voici où trouver la réponse dans la spécification liée: golang.org/ref/spec#Index_expressions
nobar

Réponses:

1476

Réponse en une ligne:

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

Explication:

ifLes instructions dans Go peuvent inclure à la fois une condition et une instruction d'initialisation. L'exemple ci-dessus utilise à la fois:

  • initialise deux variables - valrecevra soit la valeur de "foo" de la carte, soit une "valeur nulle" (dans ce cas, la chaîne vide) et okrecevra un bool qui sera défini truesi "foo" était réellement présent dans la carte

  • évalue ok, qui sera truesi "foo" était dans la carte

Si "foo" est bien présent dans la carte, le corps de l' ifinstruction sera exécuté et valsera local à cette portée.

commercialisation
la source
2
Cela peut être mieux expliqué comment cela fonctionne (comme l'autre commentaire de peterSO)
Chmouel Boudjnah
6
@Kiril var val string = ""restera le même, val, ok :=crée une nouvelle variable locale avec le même nom qui n'est visible que dans ce bloc.
OneOfOne
1
excellente réponse, diriez-vous que la complexité de ceci est O (1) ??
Mheni
1
@Mheni, je sais que je suis un peu en retard ici, mais cette question traite de la complexité de la recherche en cours. La plupart du temps, la complexité amortie est O (1) mais cela vaut la peine de lire les réponses à cette question.
3ocène
70
Une telle syntaxe déroutante par rapport à celle de python if key in dict.
Pranjal Mittal
131

En plus de la spécification du langage de programmation Go , vous devriez lire Effective Go . Dans la section sur les cartes , ils disent, entre autres:

Une tentative de récupération d'une valeur de carte avec une clé qui n'est pas présente dans la carte retournera la valeur zéro pour le type des entrées de la carte. Par exemple, si la carte contient des entiers, la recherche d'une clé inexistante retournera 0. Un ensemble peut être implémenté comme une carte avec le type de valeur bool. Définissez l'entrée de carte sur true pour placer la valeur dans l'ensemble, puis testez-la par simple indexation.

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

Parfois, vous devez distinguer une entrée manquante d'une valeur nulle. Y a-t-il une entrée pour "UTC" ou est-ce 0 parce que ce n'est pas du tout dans la carte? Vous pouvez discriminer avec une forme d'affectation multiple.

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

Pour des raisons évidentes, cela s'appelle l'idiome «comma ok». Dans cet exemple, si tz est présent, les secondes seront définies de manière appropriée et ok sera vrai; sinon, les secondes seront mises à zéro et ok sera faux. Voici une fonction qui le rassemble avec un joli rapport d'erreur:

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

Pour tester la présence dans la carte sans vous soucier de la valeur réelle, vous pouvez utiliser l'identifiant vide (_) à la place de la variable habituelle pour la valeur.

_, present := timeZone[tz]
peterSO
la source
58

Recherche sur la liste de diffusion de go-nuts et a trouvé une solution publiée par Peter Froehlich le 15/11/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")
        }
}

Ou, de façon plus compacte,

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

Notez qu'en utilisant cette forme de l' ifinstruction, les variables valueet okne sont visibles qu'à l'intérieur des ifconditions.

grokus
la source
21
Si vous voulez vraiment savoir si la clé existe ou non et que vous ne vous souciez pas de la valeur, vous pouvez l'utiliser _, ok := dict["baz"]; ok. La _pièce jette la valeur au lieu de créer une variable temporaire.
Matthew Crumley
26

Réponse courte

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

Exemple

Voici un exemple sur le Go Playground .

Réponse plus longue

Selon la section Cartes d' Efficace Go :

Une tentative de récupération d'une valeur de carte avec une clé qui n'est pas présente dans la carte retournera la valeur zéro pour le type des entrées de la carte. Par exemple, si la carte contient des entiers, la recherche d'une clé inexistante retournera 0.

Parfois, vous devez distinguer une entrée manquante d'une valeur nulle. Y a-t-il une entrée pour "UTC" ou s'agit-il de la chaîne vide parce qu'elle n'est pas du tout dans la carte? Vous pouvez discriminer avec une forme d'affectation multiple.

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

Pour des raisons évidentes, cela s'appelle l'idiome «comma ok». Dans cet exemple, si tz est présent, les secondes seront définies de manière appropriée et ok sera vrai; sinon, les secondes seront mises à zéro et ok sera faux. Voici une fonction qui le rassemble avec un joli rapport d'erreur:

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

Pour tester la présence dans la carte sans vous soucier de la valeur réelle, vous pouvez utiliser l'identifiant vide (_) à la place de la variable habituelle pour la valeur.

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

Comme indiqué dans d'autres réponses, la solution générale consiste à utiliser une expression d'index dans une affectation du formulaire spécial:

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

C'est agréable et propre. Il a cependant quelques restrictions: il doit s'agir d'une cession de forme spéciale. L'expression de droite doit être uniquement l'expression d'index de mappage et la liste des expressions de gauche doit contenir exactement 2 opérandes, le premier auquel le type de valeur est attribuable et le second auquel une boolvaleur est attribuable. La première valeur du résultat de ce formulaire spécial sera la valeur associée à la clé, et la deuxième valeur indiquera s'il existe réellement une entrée dans la carte avec la clé donnée (si la clé existe dans la carte). La liste des expressions de gauche peut également contenir l' identifiant vide si l'un des résultats n'est pas nécessaire.

Il est important de savoir que si la valeur de carte indexée nilcontient ou ne contient pas la clé, l'expression d'index est évaluée à la valeur zéro du type de valeur de la carte. Ainsi, par exemple:

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

Essayez-le sur le Go Playground .

Donc, si nous savons que nous n'utilisons pas la valeur zéro dans notre carte, nous pouvons en profiter.

Par exemple, si le type de valeur est string, et nous savons que nous ne stockons jamais d'entrées dans la carte où la valeur est la chaîne vide (valeur nulle pour le stringtype), nous pouvons également tester si la clé est dans la carte en comparant les non spéciales forme de (résultat de) l'expression d'index à la valeur zéro:

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

Sortie (essayez-la sur le Go Playground ):

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

Dans la pratique, il existe de nombreux cas où nous ne stockons pas la valeur zéro dans la carte, donc cela peut être utilisé assez souvent. Par exemple, les interfaces et les types de fonctions ont une valeur nulle nil, que nous ne stockons souvent pas dans les cartes. Il est donc possible de tester si une clé se trouve dans la carte en la comparant à nil.

L'utilisation de cette "technique" présente également un autre avantage: vous pouvez vérifier l'existence de plusieurs clés de manière compacte (vous ne pouvez pas le faire avec le formulaire spécial "virgule ok"). En savoir plus: Vérifiez si la clé existe dans plusieurs cartes dans une seule condition

L'obtention de la valeur zéro du type de valeur lors de l'indexation avec une clé non existante nous permet également d'utiliser des cartes avec des boolvaleurs de manière pratique sous forme d' ensembles . Par exemple:

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

Il sort (essayez-le sur le Go Playground ):

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

Voir aussi: Comment puis-je créer un tableau contenant des chaînes uniques?

icza
la source
1
ce qui est Ten var v, ok T = a[x]? ne okdoit pas être bool?
Kokizzu
2
@Kokizzu C'est la forme générale de la déclaration de variable. Au début, nous pourrions penser que cela ne fonctionnerait (compiler) que si la carte était de type map[bool]boolet l' Test bool, mais cela fonctionne aussi si la carte est de type map[interface{}]boolet l' Test interface{}; en outre, il fonctionne également avec des types personnalisés ayant boolcomme type sous-jacent, tout voir sur le Go Playground . Donc, puisque ce formulaire est valide avec plusieurs types substitués T, c'est pourquoi le général Test utilisé. Le type de okpeut être tout ce à quoi un non typébool peut être affecté.
icza
7

meilleure façon ici

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

Ensuite, lancez maps.go somestring existe? vrai n'existe pas? faux

Lady_Exotel
la source
Se débarrasse du besoin d'int
Lady_Exotel
Merci pour la contribution, mais je pense que les réponses actuelles couvrent bien la question. D'après ce que vous dites ici, votre réponse correspondrait mieux à la meilleure façon d'implémenter le type de question de Go .
tomasz
_, ok = m["somestring"]devrait être=_, ok := m["somestring"]
Elroy Jetson
3

Il est mentionné sous "Expressions d'index" .

Une expression d'index sur une carte a de type carte [K] V utilisée dans une affectation ou une initialisation du formulaire spécial

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

donne une valeur booléenne supplémentaire non typée. La valeur de ok est vraie si la clé x est présente dans la carte et fausse sinon.

mroman
la source
1

Une affectation à deux valeurs peut être utilisée à cet effet. Veuillez consulter mon exemple de programme ci-dessous

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
la source