¿Cómo verificar si un mapa contiene una clave en Ir?

Respuestas:

1476

Respuesta de una línea:

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

Explicación:

ifLas declaraciones en Go pueden incluir tanto una condición como una declaración de inicialización. El ejemplo anterior usa ambos:

  • inicializa dos variables: valrecibirá el valor de "foo" del mapa o un "valor cero" (en este caso, la cadena vacía) y okrecibirá un bool que se establecerá truesi "foo" estaba realmente presente en el mapa

  • evalúa ok, que será truesi "foo" estaba en el mapa

Si "foo" está realmente presente en el mapa, el cuerpo de la ifdeclaración se ejecutará y valserá local en ese ámbito.

vendedor
fuente
2
Esto puede explicarse mejor cómo funciona (como el otro comentario de peterSO)
Chmouel Boudjnah
66
@Kiril var val string = ""seguirá siendo el mismo, val, ok :=crea una nueva variable local con el mismo nombre que solo es visible en ese bloque.
OneOfOne
1
gran respuesta, ¿dirías que la complejidad de esto es O (1)?
Mheni
1
@Mheni, sé que llego un poco tarde aquí, pero esta pregunta discute la complejidad de la búsqueda. La mayoría de las veces la complejidad amortizada es O (1), pero vale la pena leer las respuestas a esa pregunta.
3ocene
70
Una sintaxis tan confusa en comparación con la de Python if key in dict.
Pranjal Mittal
131

Además de la especificación del lenguaje de programación The Go , debe leer Effective Go . En la sección de mapas , dicen, entre otras cosas:

Un intento de obtener un valor de mapa con una clave que no está presente en el mapa devolverá el valor cero para el tipo de las entradas en el mapa. Por ejemplo, si el mapa contiene números enteros, buscar una clave inexistente devolverá 0. Un conjunto puede implementarse como un mapa con valor tipo bool. Establezca la entrada del mapa en true para poner el valor en el conjunto y luego pruébelo mediante una indexación simple.

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

Algunas veces necesita distinguir una entrada faltante de un valor cero. ¿Hay una entrada para "UTC" o es 0 porque no está en el mapa? Puede discriminar con una forma de asignación múltiple.

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

Por razones obvias, esto se llama modismo de "coma ok". En este ejemplo, si tz está presente, los segundos se establecerán adecuadamente y ok será verdadero; si no, los segundos se pondrán a cero y ok será falso. Aquí hay una función que lo combina con un buen informe de error:

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

Para comprobar la presencia en el mapa sin preocuparse por el valor real, puede usar el identificador en blanco (_) en lugar de la variable habitual para el valor.

_, present := timeZone[tz]
PeterSO
fuente
58

Busqué en la lista de correos electrónicos y encontré una solución publicada por Peter Froehlich el 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")
        }
}

O, más compactamente,

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

Tenga en cuenta que, utilizando esta forma de la ifdeclaración, las variables valuey oksolo son visibles dentro de las ifcondiciones.

grokus
fuente
21
Si realmente está interesado en saber si la clave existe o no, y no le importa el valor, puede usarla _, ok := dict["baz"]; ok. La _parte arroja el valor en lugar de crear una variable temporal.
Matthew Crumley el
26

Respuesta corta

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

Ejemplo

Aquí hay un ejemplo en Go Playground .

Respuesta larga

Según la sección de Mapas de Effective Go :

Un intento de obtener un valor de mapa con una clave que no está presente en el mapa devolverá el valor cero para el tipo de las entradas en el mapa. Por ejemplo, si el mapa contiene enteros, buscar una clave inexistente devolverá 0.

Algunas veces necesita distinguir una entrada faltante de un valor cero. ¿Hay una entrada para "UTC" o es la cadena vacía porque no está en el mapa? Puede discriminar con una forma de asignación múltiple.

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

Por razones obvias, esto se llama modismo de "coma ok". En este ejemplo, si tz está presente, los segundos se establecerán adecuadamente y ok será verdadero; si no, los segundos se pondrán a cero y ok será falso. Aquí hay una función que lo combina con un buen informe de error:

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

Para comprobar la presencia en el mapa sin preocuparse por el valor real, puede usar el identificador en blanco (_) en lugar de la variable habitual para el valor.

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

Como se señaló en otras respuestas, la solución general es usar una expresión de índice en una asignación de la forma especial:

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

Esto es lindo y limpio. Sin embargo, tiene algunas restricciones: debe ser una asignación de forma especial. La expresión del lado derecho debe ser solo la expresión del índice del mapa, y la lista de expresiones del lado izquierdo debe contener exactamente 2 operandos, el primero al que se puede asignar el tipo de valor y el segundo al que boolse puede asignar un valor. El primer valor del resultado de esta forma especial será el valor asociado con la clave, y el segundo valor indicará si realmente hay una entrada en el mapa con la clave dada (si la clave existe en el mapa). La lista de expresiones del lado izquierdo también puede contener el identificador en blanco si uno de los resultados no es necesario.

Es importante saber que si el valor del mapa indexado es nilo no contiene la clave, la expresión del índice se evalúa como el valor cero del tipo de valor del mapa. Así por ejemplo:

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

Pruébalo en Go Playground .

Entonces, si sabemos que no usamos el valor cero en nuestro mapa, podemos aprovechar esto.

Por ejemplo, si el tipo de valor es string, y sabemos que nunca almacenamos entradas en el mapa donde el valor es la cadena vacía (valor cero para el stringtipo), también podemos probar si la clave está en el mapa comparando los valores no especiales forma de (resultado de) la expresión de índice al valor cero:

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

Salida (pruébalo en Go Playground ):

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

En la práctica, hay muchos casos en los que no almacenamos el valor de valor cero en el mapa, por lo que puede usarse con bastante frecuencia. Por ejemplo, las interfaces y los tipos de función tienen un valor cero nil, que a menudo no almacenamos en los mapas. Por lo tanto, se puede probar si una clave está en el mapa comparándola con nil.

El uso de esta "técnica" también tiene otra ventaja: puede verificar la existencia de varias claves de manera compacta (no puede hacerlo con el formulario especial "coma ok"). Más sobre esto: compruebe si la clave existe en varios mapas en una condición

Obtener el valor cero del tipo de valor cuando se indexa con una clave no existente también nos permite usar mapas con boolvalores convenientemente como conjuntos . Por ejemplo:

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

Sale (pruébalo en Go Playground ):

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

Ver relacionado: ¿Cómo puedo crear una matriz que contenga cadenas únicas?

icza
fuente
1
lo que es Ten var v, ok T = a[x]? no okdebe ser bool?
Kokizzu
2
@Kokizzu Esa es la forma general de la declaración de variable. Al principio podríamos pensar que solo funcionaría (compilaría) si el mapa fuera de tipo map[bool]booly Tes bool, pero también funciona si el mapa es de tipo map[interface{}]booly Tes interface{}; Además, también funciona con tipos personalizados que tienen boolcomo tipo subyacente, ver todo en Go Playground . Entonces, dado que esa forma es válida con múltiples tipos sustituidos T, es por eso que Tse usa el general . Tipo de okpuede ser cualquier cosa a la que se le pueda asignar un sin tipobool .
icza
7

mejor manera aquí

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

Luego, ve a ejecutar maps.go somestring existe? verdad no existe? falso

Lady_Exotel
fuente
Se deshace de la necesidad de int
Lady_Exotel
Gracias por la contribución, pero creo que las respuestas actuales cubren bien la pregunta. Por lo que está diciendo aquí, su respuesta encajaría mejor con la mejor forma de implementar un conjunto de preguntas tipo Go .
tomasz
_, ok = m["somestring"]debería ser=_, ok := m["somestring"]
Elroy Jetson el
3

Se menciona en "Expresiones de índice" .

Una expresión de índice en un mapa a de tipo mapa [K] V usado en una asignación o inicialización de la forma especial

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

produce un valor booleano sin tipo adicional. El valor de ok es verdadero si la clave x está presente en el mapa, y falso en caso contrario.

mroman
fuente
1

Se puede usar una asignación de dos valores para este propósito. Por favor revise mi programa de muestra a continuación

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
fuente