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

Счётчик и сумма по группе

Две самые частые работы map на складе: посчитать, сколько раз встретился каждый артикул, и сложить количества по артикулу. Обе пишутся одной строкой — потому что чтение отсутствующего ключа отдаёт ноль.


counts[sku]++

Ситуация. Задача с первой страницы: сколько коробок каждого артикула. Срезами там понадобились цикл в цикле и два среза. С map весь подсчёт — цикл из трёх строк. Сначала предскажите, что он насчитает.

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

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

1. Не запуская, прочитайте программу. counts[sku]++ значит то же, что counts[sku] = counts[sku] + 1.

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

import "fmt"

func main() {
	counts := map[string]int{}
	skus := []string{"K-417", "M-120", "K-417", "K-417", "T-118", "M-120"}
	for _, sku := range skus {
		counts[sku]++
	}
	fmt.Println(len(counts), counts["M-120"])
}

2. Сдайте два числа через пробел:

▶ Выполните · выделенное замените своим
course answer day18.q3 ЧИСЛО ЧИСЛО

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

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

import "fmt"

func main() {
	counts := map[string]int{}
	skus := []string{"K-417", "M-120", "K-417", "K-417", "T-118", "M-120"}
	for _, sku := range skus {
		counts[sku]++
	}
	fmt.Println(len(counts), counts["M-120"])
}
▶ Выполните
cd ~/gocourse/day18/scratch
go run .

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

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


Что увидите.

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

Как это читать. В колонке counts — то, что напечатал бы fmt.Println(counts) после строки counts[sku]++:

проход sku что сделала строка counts после
1 K-417 ключа нет: прочитали 0, записали 1 map[K-417:1]
2 M-120 ключа нет: 0 + 1 map[K-417:1 M-120:1]
3 K-417 ключ есть: 1 + 1 map[K-417:2 M-120:1]
4 K-417 2 + 1 map[K-417:3 M-120:1]
5 T-118 ключа нет: 0 + 1 map[K-417:3 M-120:1 T-118:1]
6 M-120 1 + 1 map[K-417:3 M-120:2 T-118:1]

Проверки «встречался ли артикул» нет и не нужно: для нового артикула чтение отдаёт ноль, ноль плюс один — единица, запись создаёт пару. Разных артикулов три, M-120 — дважды.


Сумма по группе

Ситуация. У каждой коробки есть количество штук, нужна сумма по артикулу. Приём тот же, вместо ++ — +=:

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

import "fmt"

func main() {
	skus := []string{"P-305", "K-417", "P-305", "M-120"}
	qtys := []int{4, 10, 6, 1}
	totals := map[string]int{}
	for i, sku := range skus {
		totals[sku] += qtys[i]
	}
	fmt.Println(totals["P-305"], totals["K-417"], totals["M-120"])
}

Что увидите.

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

Как это читать. skus[i] и qtys[i] — одна коробка; for i, sku := range skus из дня 16 отдаёт номер и артикул. totals[sku] += qtys[i] — «прочитать сумму (или ноль), прибавить, записать»: у P-305 4 + 6 = 10.

Тонкость для задания group: запись создаёт пару, даже если прибавили ноль. После totals["C-3"] += 0 ключ C-3 в map есть, и len его считает.


Со ввода — в map

Чтение строк до конца ввода было в дне 08; счётчик встаёт в тот же цикл.

Попробуйте сейчас: счётчик со ввода.

Цель: программа считает артикулы, поданные на ввод, и печатает map целиком.

1. Наберите в черновик:

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

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

func main() {
	counts := map[string]int{}
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		sku := strings.TrimSpace(scanner.Text())
		counts[sku]++
	}
	fmt.Println(len(counts), counts)
}

2. Подайте три строки через конвейер:

▶ Выполните
cd ~/gocourse/day18/scratch
printf 'K-417\nM-120\nK-417\n' | go run .

3. Подайте то же с пустой строкой посередине:

▶ Выполните
printf 'K-417\n\nK-417\n' | go run .

Готово, когда: на шаге 2 напечатано 2 map[K-417:2 M-120:1], а на шаге 3 вы видите лишнюю пару и знаете, откуда она (таблица ниже).


Печать map через Println годится, чтобы посмотреть, что насчитано. Отчёт печатают по ключам сами — и тут встаёт вопрос порядка: следующая страница.


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

Что видите Что это значит Что делать
2 map[:1 K-417:2] на вводе с пустой строкой пустая строка стала ключом "" после TrimSpace: if sku == "" { continue }
у каждого артикула ровно 1 написано counts[sku] = 1 counts[sku]++
сумма равна последнему количеству написано totals[sku] = qtys[i] totals[sku] += qtys[i]
один артикул — две пары в строке пробел в конце: "K-417 " — другой ключ strings.TrimSpace перед записью

Дальше: в каком порядке обходится map и как напечатать отчёт по возрастанию — course next