Як перевірити, чи карта містить ключ у програмі Go?


761

Я знаю, що можу повторити карту m,

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

і шукати ключ, але чи є більш ефективний спосіб перевірити існування ключа на карті?

Я не зміг знайти відповідь у мовній специфікації .


2
Ось де знайти відповідь у пов'язаній специфікації: golang.org/ref/spec#Index_expressions
nobar

Відповіді:


1471

Відповідь в одному рядку:

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

Пояснення:

ifОператори в Go можуть включати як умову, так і ініціалізацію. У наведеному вище прикладі використовуються обидва:

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

  • оцінює ok, що буде, trueякщо на карті було "foo"

Якщо "foo" дійсно присутній на карті, тіло ifоператора буде виконано і valбуде локальним для цього обсягу.


2
Це може бути краще пояснено, як це працює (як і інший коментар від peterSO)
Chmouel Boudjnah

6
@Kiril var val string = ""залишиться тим самим, val, ok :=створює нову локальну змінну з тим самим іменем, яка лише видима в цьому блоці.
OneOfOne

1
чудова відповідь, ви б сказали, складність цього є O (1) ??
Mheni

1
@Mheni, я знаю, я трохи запізнююся тут, але це питання обговорює складність пошуку в дорозі. Більшу частину часу амортизована складність становить O (1), але варто прочитати відповіді на це питання.
3оцен

70
Такий заплутаний синтаксис порівняно з python if key in dict.
Пранджал Міттал

129

Окрім специфікації мови програмування Go , слід прочитати « Ефективний перехід» . У розділі про карти , серед іншого, вони говорять:

Спроба отримати значення карти за допомогою ключа, якого немає на карті, поверне нульове значення для типу записів на карті. Наприклад, якщо карта містить цілі числа, пошук неіснуючого ключа поверне 0. Набір може бути реалізований у вигляді карти із значенням типу bool. Встановіть на карті значення true, щоб поставити значення у набір, а потім протестуйте його простим індексуванням.

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

Іноді потрібно відрізняти пропущений запис від нульового значення. Чи є запис для "UTC" чи це 0, оскільки його взагалі немає на карті? Ви можете розрізнити форму множинного призначення.

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

З очевидних причин це називається ідіомою «кома в порядку». У цьому прикладі, якщо присутній tz, секунди будуть встановлені відповідним чином, і нормально буде вірно; якщо ні, секунди будуть встановлені на нуль, а нормально - помилково. Ось функція, яка поєднує його з приємним звітом про помилку:

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

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

_, present := timeZone[tz]

57

Шукали у списку електронних листів з горіхами та знайшли рішення, розміщене Peter Froehlich 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")
        }
}

Або, більш компактно,

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

Зверніть увагу, що з допомогою цієї форми ifзаяви, то valueі okзмінні видно тільки всередині ifумов.


20
Якщо вас дійсно просто цікавить, чи існує ключ чи ні, і вам не важливо значення, ви можете використовувати _, ok := dict["baz"]; ok. _Частина кидає значення геть замість створення тимчасової змінної.
Меттью Крамлі

26

Коротка відповідь

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

Приклад

Ось приклад на майданчику Go Go .

Більш довгий відповідь

Згідно з розділом " Ефективний перехід" на Картах :

Спроба отримати значення карти за допомогою ключа, якого немає на карті, поверне нульове значення для типу записів на карті. Наприклад, якщо карта містить цілі числа, пошук неіснуючого ключа поверне 0.

Іноді потрібно відрізняти пропущений запис від нульового значення. Чи є запис для "UTC" чи це порожній рядок, оскільки його взагалі немає на карті? Ви можете розрізнити форму множинного призначення.

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

З очевидних причин це називається ідіомою «кома в порядку». У цьому прикладі, якщо присутній tz, секунди будуть встановлені відповідним чином, і нормально буде вірно; якщо ні, секунди будуть встановлені на нуль, а нормально - помилково. Ось функція, яка поєднує його з приємним звітом про помилку:

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

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

_, present := timeZone[tz]

13

Як зазначають інші відповіді, загальним рішенням є використання індексного вираження у призначенні спеціальної форми:

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типу), ми також можемо перевірити, чи є ключ на карті, порівнюючи неспеціальне форма вираження (результат) індексу до нульового значення:

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 ):

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

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

Використання цієї "техніки" має ще одну перевагу: ви можете перевірити існування декількох клавіш компактно (це не можна зробити за допомогою спеціальної форми "кома нормально"). Більше про це: Перевірте, чи існує ключ у кількох картах в одній умові

Отримання нульового значення типу значення при індексації за допомогою неіснуючого ключа також дозволяє використовувати карти зі boolзначеннями зручно як набори . Наприклад:

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

Він виводить (спробуйте на майданчику Go ):

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

Див. Пов'язане: Як я можу створити масив, який містить унікальні рядки?


1
що Tв var v, ok T = a[x]? не okповинен бути bool?
Кокідзу

2
@Kokizzu Це загальна форма оголошення змінної. Спочатку ми можемо подумати, що це буде працювати (компілювати) лише тоді, коли карта буде типовою map[bool]boolта Tє bool, але вона також працює, якщо карта має тип map[interface{}]boolі Tє interface{}; крім того, він також працює з типовими типами, що мають boolбазовий тип, дивіться все на майданчику Go . Оскільки ця форма є дійсною для декількох типів, замінених на них T, тому використовується загальна T. Тип okможе бути будь-яким, до якого може бути призначений нетипізованийbool .
icza


4
    var d map[string]string
    value, ok := d["key"]
    if ok {
        fmt.Println("Key Present ", value)
    } else {
        fmt.Println(" Key Not Present ")
    }

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)

Потім запустіть запустити maps.go існує рядка? правда не існує? помилковий


Позбавляється від потреби в int
Lady_Exotel

Дякую за внесок, але, думаю, нинішні відповіді добре висвітлюють це питання. З того, що ви тут говорите, ваша відповідь більше відповідатиме найкращому способу реалізації заданого типу запитань Go .
tomasz

_, ok = m["somestring"]має бути=_, ok := m["somestring"]
Елрой

3

Він згадується у розділі "Індекси виразів" .

Індексний вираз на карті a типу типу [K] V, який використовується в призначенні або ініціалізації спеціальної форми

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

дає додаткове нетипізоване булеве значення. Значення ОК вірно, якщо ключ x присутній на карті, а помилкове - в іншому випадку.


1

Для цього можна використовувати двозначне призначення. Будь ласка, перевірте мою зразок програми нижче

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")
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.