Как проверить, есть ли на карте ключ?

Я знаю, что могу перебрать карту m от,

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

и искать ключ, но есть ли более эффективный способ проверки существования ключа на карте?

Я не мог найти ответ в спецификации языка.

13 ответов

Решение

Ответ в одну строку:

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

Объяснение:

if Операторы в Go могут включать как условие, так и оператор инициализации. В приведенном выше примере используются оба:

  • инициализирует две переменные - val получит либо значение "foo" с карты, либо "нулевое значение" (в данном случае пустую строку) и ok получит bool, который будет установлен в true если "foo" действительно присутствовал на карте

  • оценивает ok, которые будут true если "Foo" был на карте

Если "foo" действительно присутствует на карте, тело if заявление будет выполнено и val будет локальным для этой области.

Редактировать: следующий ответ до перехода к Go 1. Начиная с Go 1, он больше не является точным / действительным.


В дополнение к спецификации языка программирования Go вы должны прочитать Effective Go. В разделе о картах, среди прочего, говорится:

"Попытка получить значение карты с ключом, которого нет на карте, приведет к сбою программы, но есть способ сделать это безопасно, используя множественное назначение".

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

"Чтобы проверить наличие на карте, не беспокоясь о фактическом значении, вы можете использовать пустой идентификатор, простое подчеркивание (_). Пустой идентификатор может быть назначен или объявлен с любым значением любого типа, причем значение отбрасывается безвредно. Для проверки присутствия на карте используйте пустой идентификатор вместо обычной переменной для значения."

_, present := timeZone[tz]

Поиск по списку рассылок и поиск решения, размещенного Питером Фрёлихом 15/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")
        }
}

Или, более компактно,

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

Обратите внимание, используя эту форму if заявление, value а также ok переменные видны только внутри if условия.

Короткий ответ

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

пример

Вот пример на игровой площадке Go.

Более длинный ответ

В разделе " Карты " Effective Go:

Попытка получить значение карты с ключом, которого нет на карте, вернет нулевое значение для типа записей в карте. Например, если карта содержит целые числа, поиск несуществующего ключа вернет 0.

Иногда вам нужно отличить отсутствующую запись от нулевого значения. Есть ли запись для "UTC" или это пустая строка, потому что ее нет на карте вообще? Вы можете различать с помощью формы множественного назначения.

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

По понятным причинам это называется "запятая нормально". В этом примере, если tz присутствует, секунды будут установлены соответствующим образом, и ok будет истинным; если нет, то секунды будут установлены на ноль, и ok будет ложным. Вот функция, которая объединяет это с хорошим сообщением об ошибке:

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

Чтобы проверить наличие на карте, не беспокоясь о фактическом значении, вы можете использовать пустой идентификатор (_) вместо обычной переменной для значения.

_, present := timeZone[tz]

Взгляните на этот фрагмент кода

    nameMap := make(map[string]int)
    nameMap["river"] = 33
    v ,exist := nameMap["river"]
    if exist {
        fmt.Println("exist ",v)
    }
    var d map[string]string
    value, ok := d["key"]
    if ok {
        fmt.Println("Key Present ", value)
    } else {
        fmt.Println(" Key Not Present ")
    }

Как отмечено другими ответами, общее решение состоит в том, чтобы использовать индексное выражение в присваивании специальной формы:

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

Это красиво и чисто. Однако у него есть некоторые ограничения: это должно быть присвоение специальной формы. Выражение в правой части должно быть только выражением индекса карты, а список выражений в левой части должен содержать ровно 2 операнда, первый из которых может присваивать тип значения, а второй - bool значение присваивается Первым значением результата этого специального выражения индекса формы будет значение, связанное с ключом, а второе значение скажет, существует ли на карте запись с данным ключом (если ключ существует на карте). Список выражений в левой части также может содержать пустой идентификатор, если один из результатов не нужен.

Важно знать, что если индексированное значение карты nil или не содержит ключ, индексное выражение оценивается как нулевое значение типа значения карты. Так, например:

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

Попробуйте это на игровой площадке Go.

Поэтому, если мы знаем, что не используем нулевое значение на нашей карте, мы можем воспользоваться этим.

Например, если тип значения stringи мы знаем, что никогда не сохраняем записи на карте, где значение является пустой строкой (нулевое значение для string type), мы также можем проверить, находится ли ключ на карте, сравнив несвойственную форму (результат) выражения index с нулевым значением:

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

Вывод (попробуйте на Go Playground):

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

На практике существует много случаев, когда мы не храним нулевое значение на карте, поэтому это можно использовать довольно часто. Например, интерфейсы и типы функций имеют нулевое значение nil, который мы часто не храним в картах. Таким образом, можно проверить, есть ли ключ на карте, сравнив его с nil,

Использование этой "техники" имеет и другое преимущество: вы можете компактно проверить наличие нескольких ключей (вы не можете сделать это с помощью специальной формы "запятая нормально"). Подробнее об этом: Проверьте, существует ли ключ на нескольких картах в одном условии

Лучший способ здесь

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)

Тогда иди запусти maps.go что-нибудь существует? правда не существует? ложный

Упоминается в разделе "Индексные выражения".

Индексное выражение на карте a типа map[K]V, используемое в присваивании или инициализации специальной формы

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

дает дополнительное нетипизированное логическое значение. Значение ok равно true, если ключ x присутствует на карте, и false в противном случае.

Для этой цели можно использовать присвоение двух значений. Пожалуйста, проверьте мой пример программы ниже

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

Пример использования: Цикл по срезу, для проверки PairMap, существует ли ключ. Это алгоритм для поиска всех пар, которые складываются в определенную сумму.

      func findPairs(slice1 []int, sum int) {
    pairMap := make(map[int]int)
    for i, v := range slice1 {
        if valuei, ok := pairMap[v]; ok {
            fmt.Println("Pair Found", i, valuei)
        } else {
            pairMap[sum-v] = i
        }
    }
}

Просто используйте

if len(m) == 0 {
    ...
}
Другие вопросы по тегам