Есть ли ключ
В прошлом уроке мы выяснили: если имени в телефонной книге нет, map отвечает нулевым значением. Обычно это удобно, но иногда мешает. В map с очками игроков значение 0 может быть настоящим результатом. По обычному чтению его не отличить от ключа, которого нет:
package main
import "fmt"
func main() {
scores := map[string]int{"ann": 0, "bob": 7}
fmt.Println(scores["ann"], scores["max"]) // 0 0
}Здесь два разных случая:
- игрок
annсыграл и набрал ноль очков; - игрока
maxвmapнет вовсе.
Оба чтения возвращают 0, потому что отсутствующий ключ читается как нулевое значение. Это всё равно что спросить телефонную книгу о номере, а в ответ услышать «0» и не понять: у человека такой номер или его нет в книге.
Два значения при чтении
Чтение из map может вернуть второе значение типа bool. Оно равно true, если ключ есть, и false, если ключа нет. Такую запись называют идиомой comma ok — по запятой между двумя переменными:
scores := map[string]int{"ann": 0, "bob": 7}
v, ok := scores["ann"]
fmt.Println(v, ok) // 0 true
w, found := scores["max"]
fmt.Println(w, found) // 0 falseДавайте разберём пример по строкам:
v, ok := scores["ann"]читает два значения. Ключ"ann"есть, поэтомуokравноtrue, хотяvравно0: важно, есть ли ключ, а не какое у него значение.fmt.Println(v, ok)печатает0 true.w, found := scores["max"]читает отсутствующий ключ. Первое значение нулевое, как при обычном чтении, а второе —false.fmt.Println(w, found)печатает0 false.
Имя второй переменной может быть любым: ok — договорённость, а не ключевое слово. Если само значение не нужно, вместо первой переменной пишут _: _, ok := scores["max"].
Этот вопрос часто задают на собеседовании: «Как проверить, есть ли ключ в map?» Ответ — прочитать два значения и посмотреть на второе.
Когда нужна comma ok
Выбор зависит от того, бывает ли нулевое значение настоящим:
- бывает — ноль очков, пустое имя,
falseвmap[string]bool— тогда нужна comma ok; - не бывает — например, цена товара всегда больше нуля — тогда хватает обычного чтения и сравнения с нулём.
Проверка в if
Чаще всего comma ok стоит в инициализации if. Переменные v и ok тогда видны только внутри if и не мешают остальному коду:
scores := map[string]int{"bob": 7}
if v, ok := scores["bob"]; ok {
fmt.Println("bob:", v) // bob: 7
}
if _, ok := scores["max"]; !ok {
fmt.Println("no max") // no max
}Давайте разберём оба if:
- Ключ
"bob"есть, поэтому первыйifпечатает его очки:bob: 7. - Ключа
"max"нет,okравноfalse, и второйifпечатаетno max.
map как множество
Иногда нулевое значение для отсутствующего ключа как раз помогает. Так устроено множество — набор значений без повторов. Отдельного типа для множества в Go нет, и на собеседовании часто спрашивают, как его сделать. Ответ — map со значениями bool: seen := map[string]bool{}.
Работают с таким множеством так:
- значение добавляют записью
seen[w] = true; - проверяют обычным чтением:
if seen[w]; - значение
falseв такуюmapникто не пишет, поэтомуfalseпри чтении означает одно: этого ключа в множестве нет.
Comma ok здесь не нужна. Так удобно отсеивать повторы: если seen[w] уже true, значение встречалось раньше.
Частая ошибка
Значение сравнивают с нулём, чтобы узнать, есть ли ключ:
scores := map[string]int{"ann": 0}
if scores["ann"] == 0 { // ✗
fmt.Println("no ann")
}Программа печатает no ann, хотя ключ "ann" в map есть: его значение 0 совпадает с нулевым значением для отсутствующего ключа. Такая проверка путает игрока с нулём очков и игрока, которого нет. Проверяйте второе значение: if _, ok := scores["ann"]; !ok.
Главное
v, ok := m[k]возвращает значение иtrue, если ключ есть, или нулевое значение иfalse, если его нет.- Comma ok нужна, когда нулевое значение бывает настоящим, как ноль очков.
- Множество в Go —
map[T]bool: добавляют черезseen[x] = true, проверяют черезif seen[x].
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.