A dictionary: key → value and comma-ok
A slice answers "what's the 3rd item?". But real programs ask a different question: "how many are in the history category?", "which item has this exact title?". Scanning the whole slice every time is slow and clumsy. A map answers by key, instantly.
Think of a paper dictionary: you don't read every page — you jump straight to the word. A Go map is the same: a key → value table.
Create one with make:
ages := make(map[string]int) // key: string, value: int — empty to start
ages["Jonas"] = 30 // set
ages["Rūta"] = 25
fmt.Println(ages["Jonas"]) // 30 — read by key
map[string]int reads left to right: a map whose keys are strings and whose values are ints. The keys can be any comparable type (string, int…); the values can be anything — even a struct: map[string]Item.
The trap: a missing key doesn't crash — it returns the zero value.
fmt.Println(ages["Nobody"]) // 0 — NOT an error, just int's zero value
So ages["Nobody"] gives 0, and for a map[string]string a missing key gives "". That's convenient, but it means you can't tell "the value is really 0" from "the key isn't there". For that, Go has the comma-ok idiom:
age, ok := ages["Rūta"]
if ok {
fmt.Println("Rūta is", age) // ok == true → the key exists
} else {
fmt.Println("no such person") // ok == false → it doesn't
}
Read it as: "give me the value and whether it was there." You'll use v, ok := m[k] constantly — it's the safe way to look something up.
Counting is a one-liner. Because a missing key reads as 0, you can ++ straight away:
counts := make(map[string]int)
counts["history"]++ // history wasn't there → 0, then +1 → 1
counts["history"]++ // now 2
No "is it there yet?" check — the zero value does the work. This little pattern is the heart of this lesson's app.
Remove a key with delete, count with len:
delete(ages, "Jonas") // gone; deleting a missing key is a safe no-op
fmt.Println(len(ages)) // how many keys are in the map
Next: how to walk every entry with range — and the one surprise Go has waiting there.