Eu sei que posso iterar sobre um mapa m
,
for k, v := range m { ... }
e procure uma chave, mas existe uma maneira mais eficiente de testar a existência de uma chave em um mapa?
Não consegui encontrar a resposta na especificação do idioma .
Eu sei que posso iterar sobre um mapa m
,
for k, v := range m { ... }
e procure uma chave, mas existe uma maneira mais eficiente de testar a existência de uma chave em um mapa?
Não consegui encontrar a resposta na especificação do idioma .
Respostas:
Resposta em uma linha:
if val, ok := dict["foo"]; ok {
//do something here
}
if
As instruções no Go podem incluir uma condição e uma instrução de inicialização. O exemplo acima usa os dois:
inicializa duas variáveis - val
receberá o valor de "foo" do mapa ou um "valor zero" (nesse caso, a string vazia) e ok
receberá um bool que será definido como true
se "foo" estivesse realmente presente no mapa
avalia ok
, que será true
se "foo" estava no mapa
Se "foo" estiver realmente presente no mapa, o corpo da if
instrução será executado e val
será local nesse escopo.
var val string = ""
permanecerá o mesmo, val, ok :=
cria uma nova variável local com o mesmo nome, visível apenas nesse bloco.
if key in dict
.
Além da especificação da linguagem de programação The Go , você deve ler Go eficaz . Na seção de mapas , eles dizem, entre outras coisas:
Uma tentativa de buscar um valor do mapa com uma chave que não está presente no mapa retornará o valor zero para o tipo de entradas no mapa. Por exemplo, se o mapa contiver números inteiros, procurar uma chave inexistente retornará 0. Um conjunto pode ser implementado como um mapa com o tipo de valor bool. Defina a entrada do mapa como true para colocar o valor no conjunto e, em seguida, teste-o pela indexação simples.
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") }
Às vezes, você precisa distinguir uma entrada ausente de um valor zero. Existe uma entrada para "UTC" ou é 0 porque não está no mapa? Você pode discriminar com uma forma de atribuição múltipla.
var seconds int var ok bool seconds, ok = timeZone[tz]
Por razões óbvias, isso é chamado de idioma "vírgula ok". Neste exemplo, se tz estiver presente, os segundos serão definidos adequadamente e ok será verdadeiro; caso contrário, os segundos serão definidos como zero e ok será falso. Aqui está uma função que o reúne com um bom relatório de erros:
func offset(tz string) int { if seconds, ok := timeZone[tz]; ok { return seconds } log.Println("unknown time zone:", tz) return 0 }
Para testar a presença no mapa sem se preocupar com o valor real, você pode usar o identificador em branco (_) no lugar da variável usual para o valor.
_, present := timeZone[tz]
Pesquisou na lista de e -mails desagradáveis e encontrou uma solução publicada por Peter Froehlich em 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 forma mais compacta,
if value, ok := dict["baz"]; ok {
fmt.Println("value: ", value)
} else {
fmt.Println("key not found")
}
Observe que, usando este formulário da if
instrução, as variáveis value
e ok
são visíveis apenas dentro das if
condições.
_, ok := dict["baz"]; ok
. A _
peça joga fora o valor em vez de criar uma variável temporária.
_, exists := timeZone[tz] // Just checks for key existence
val, exists := timeZone[tz] // Checks for key existence and retrieves the value
Aqui está um exemplo no Go Playground .
De acordo com a seção Mapas do Effective Go :
Uma tentativa de buscar um valor do mapa com uma chave que não está presente no mapa retornará o valor zero para o tipo de entradas no mapa. Por exemplo, se o mapa contiver números inteiros, procurar uma chave inexistente retornará 0.
Às vezes, você precisa distinguir uma entrada ausente de um valor zero. Existe uma entrada para "UTC" ou essa é a string vazia porque não está no mapa? Você pode discriminar com uma forma de atribuição múltipla.
var seconds int var ok bool seconds, ok = timeZone[tz]
Por razões óbvias, isso é chamado de idioma "vírgula ok". Neste exemplo, se tz estiver presente, os segundos serão definidos adequadamente e ok será verdadeiro; caso contrário, os segundos serão definidos como zero e ok será falso. Aqui está uma função que o reúne com um bom relatório de erros:
func offset(tz string) int { if seconds, ok := timeZone[tz]; ok { return seconds } log.Println("unknown time zone:", tz) return 0 }
Para testar a presença no mapa sem se preocupar com o valor real, você pode usar o identificador em branco (_) no lugar da variável usual para o valor.
_, present := timeZone[tz]
Conforme observado por outras respostas, a solução geral é usar uma expressão de índice em uma atribuição do formulário especial:
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
var v, ok T = a[x]
Isso é legal e limpo. Porém, ele tem algumas restrições: deve ser uma atribuição de forma especial. A expressão do lado direito deve ser apenas a expressão do índice do mapa e a lista de expressões da esquerda deve conter exatamente 2 operandos, primeiro ao qual o tipo de valor é atribuível e um segundo ao qual um bool
valor é atribuível. O primeiro valor do resultado desse formulário especial será o valor associado à chave e o segundo valor informará se há realmente uma entrada no mapa com a chave especificada (se a chave existir no mapa). A lista de expressões do lado esquerdo também pode conter o identificador em branco se um dos resultados não for necessário.
É importante saber que, se o valor do mapa indexado é nil
ou não contém a chave, a expressão do índice é avaliada como o valor zero do tipo de valor do mapa. Então, por exemplo:
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
Experimente no Go Playground .
Portanto, se soubermos que não usamos o valor zero em nosso mapa, podemos tirar proveito disso.
Por exemplo, se o tipo de valor for string
e sabemos que nunca armazenamos entradas no mapa em que o valor é a sequência vazia (valor zero para o string
tipo), também podemos testar se a chave está no mapa comparando os itens não especiais forma da expressão de índice (resultado da) com o valor zero:
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] != "")
Saída (experimente no Go Playground ):
Key 0 exists: true
Key 1 exists: true
Key 2 exists: false
Na prática, existem muitos casos em que não armazenamos o valor zero no mapa, portanto, isso pode ser usado com bastante frequência. Por exemplo, interfaces e tipos de função têm um valor zero nil
, que geralmente não armazenamos em mapas. Portanto, é possível testar se uma chave está no mapa comparando-a com nil
.
O uso dessa "técnica" também tem outra vantagem: você pode verificar a existência de várias chaves de maneira compacta (não é possível fazer isso com o formulário especial "vírgula ok"). Mais sobre isso: verifique se existe chave em vários mapas em uma condição
Obter o valor zero do tipo de valor ao indexar com uma chave inexistente também nos permite usar mapas com bool
valores convenientemente como conjuntos . Por exemplo:
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")
}
Ele produz (experimente no Go Playground ):
Contains 'one': true
'two' is in the set
'three' is not in the set
Veja relacionados: Como posso criar uma matriz que contém seqüências de caracteres exclusivas?
T
dentro var v, ok T = a[x]
? não ok
deve ser booleano?
map[bool]bool
e T
é bool
, mas também funciona se o mapa for do tipo map[interface{}]bool
e T
é interface{}
; além disso, ele também funciona com tipos personalizados que têm bool
como tipo subjacente, veja tudo no Go Playground . Portanto, como esse formulário é válido com vários tipos substituídos T
, é por isso que o general T
é usado. Tipo de ok
pode ser qualquer coisa à qual um tipo não digitadobool
pode ser atribuído.
melhor caminho aqui
if _, ok := dict["foo"]; ok {
//do something here
}
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)
Então, vá em maps.go existe alguma restrição? verdadeiro não existe? falso
_, ok = m["somestring"]
deveria ser #=_, ok := m["somestring"]
É mencionado em "Expressões de índice" .
Uma expressão de índice em um mapa a do tipo map [K] V usado em uma atribuição ou inicialização do formulário especial
v, ok = a[x] v, ok := a[x] var v, ok = a[x]
gera um valor booleano não digitado adicional. O valor de ok é verdadeiro se a chave x estiver presente no mapa e falso caso contrário.
Uma atribuição de dois valores pode ser usada para essa finalidade. Verifique meu programa de amostra abaixo
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")
}
}