День 18 · map: таблица по ключу · страница 2 из 6

Есть ли ключ: v, ok и delete

Map на любой ключ отвечает значением — даже на тот, которого в ней нет. Когда ноль — законное значение, нужно знать, лежит ли пара в map на самом деле.


Ноль или «нет в каталоге»

Ситуация. В map stock — каталог склада. M-120 в каталоге есть, но закончился. T-999 в каталоге нет. Программа отвечает на запросы:

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	stock := map[string]int{"K-417": 12, "M-120": 0}
	for _, sku := range []string{"K-417", "M-120", "T-999"} {
		qty := stock[sku]
		if qty == 0 {
			fmt.Println(sku, "нет в каталоге")
		} else {
			fmt.Println(sku, "остаток", qty)
		}
	}
}

Что увидите.

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day18/scratch$ go run .
K-417 остаток 12
M-120 нет в каталоге
T-999 нет в каталоге

Как это читать. Про M-120 программа говорит неправду. Условие qty == 0 смотрит на значение, а оно у закончившегося товара и у отсутствующего одно — ноль. По значению их не различить.


Для этого у чтения из map есть форма с двумя переменными слева. Сначала предскажите, что она отдаёт.

Попробуйте сейчас: ноль или нет ключа.

Цель: предсказать четыре значения и сдать ответ.

1. Запись a, okA := stock["M-120"] читает два значения: само значение по ключу и признак true или false — есть ли такой ключ в map. Не запуская, прочитайте программу:

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	stock := map[string]int{"K-417": 12, "M-120": 0}
	a, okA := stock["M-120"]
	b, okB := stock["T-999"]
	fmt.Println(a, okA, b, okB)
}

2. Сдайте четыре значения через пробел, в порядке печати:

▶ Выполните · выделенное замените своим
course answer day18.q2 ЗНАЧЕНИЕ ЗНАЧЕНИЕ ЗНАЧЕНИЕ ЗНАЧЕНИЕ

3. Проверьте запуском:

✎ Наберите в файл scratch/main.go
package main

import "fmt"

func main() {
	stock := map[string]int{"K-417": 12, "M-120": 0}
	a, okA := stock["M-120"]
	b, okB := stock["T-999"]
	fmt.Println(a, okA, b, okB)
}
▶ Выполните
cd ~/gocourse/day18/scratch
go run .

Вывод и разбор — под блоком.

Готово, когда: пункт q2 в course check 18 зелёный.


v, ok := m[k]

Что увидите.

Вывод · это печатает программа, набирать не нужно
0 true 0 false

Как это читать.

Запись Что значит
qty, ok := stock[sku] qty — значение или нулевое, если ключа нет; ok — true, если ключ есть
ok переменная типа bool; имя — соглашение, так её зовут почти во всём коде на Go
_, ok := stock[sku] значение не нужно, нужен только ответ «есть ли ключ»

Форма знакома по n, err := strconv.Atoi(s) из дня 12, только отсутствие ключа — не ошибка, и второе значение — простое true или false.

Та же программа с проверкой ok:

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	stock := map[string]int{"K-417": 12, "M-120": 0}
	for _, sku := range []string{"K-417", "M-120", "T-999"} {
		qty, ok := stock[sku]
		if !ok {
			fmt.Println(sku, "нет в каталоге")
		} else {
			fmt.Println(sku, "остаток", qty)
		}
	}
}
Вывод · это печатает программа, набирать не нужно
K-417 остаток 12
M-120 остаток 0
T-999 нет в каталоге

Если ноль и «нет ключа» для задачи значат одно и то же — как у счётчика коробок на следующей странице, — ok не нужен. Если это разные вещи — только ok.


delete: убрать пару

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	stock := map[string]int{"K-417": 12, "M-120": 0, "P-305": 7}
	delete(stock, "M-120")
	delete(stock, "T-999")
	fmt.Println(len(stock), stock)
	_, ok := stock["M-120"]
	fmt.Println(ok)
}

Что увидите.

Вывод · это печатает программа, набирать не нужно
2 map[K-417:12 P-305:7]
false

Как это читать. delete(stock, "M-120") убирает пару целиком: длина стала 2, ok на этом ключе — false. Удаление ключа, которого нет, молча ничего не делает. delete, как и len, — встроенная функция, импорт не нужен. Обнулить значение и удалить пару — разные вещи: после stock["M-120"] = 0 ok остаётся true.


Попробуйте сейчас: починить fix2.

Цель: программа в ~/gocourse/day18/fix2 отличает артикул с остатком 0 от артикула, которого нет в каталоге.

1. Прочитайте комментарий в начале main.go и запустите на примере:

▶ Выполните
cd ~/gocourse/day18/fix2
cat example.txt
go run . < example.txt

Один из артикулов примера в каталоге есть с остатком 0, а программа говорит про него «нет в каталоге».

2. Почините две строки: чтение остатка — двумя значениями, условие — проверка наличия ключа, а не нуля. Каталог и печать не меняйте.

3. Запустите снова и посмотрите правку:

▶ Выполните
go run . < example.txt
diff ~/.course/orig/day18/fix2/main.go main.go

4. Закоммитьте:

▶ Выполните
cd ~/gocourse/day18
git add .
git commit -m "fix2: проверка ok"

Готово, когда: артикул с остатком 0 печатается как остаток 0 шт., отсутствующий — нет в каталоге, правка — не больше двух строк: пункт fix2. Проверка подаст и другие артикулы каталога.


Что может пойти не так

Что видите Что это значит Что делать
declared and not used: ok взяли ok и не проверили проверить if !ok или читать одной переменной
товар с нулевым остатком «нет в каталоге» условие смотрит на значение qty == 0 проверять ok
после delete пара на месте ключ в delete набран с опечаткой, и delete промолчал напечатать map до и после, сверить ключ
fix2: изменено строк больше двух переписано больше нужного cp ~/.course/orig/day18/fix2/main.go main.go и поправить две строки

Дальше: map как счётчик и как сумма по группе — course next