Maps
The other big job PHP's array does is the associative array: ['Bank' => 3, 'Moorgate' => 1]. In Go that's a map, and its type spells out both halves: map[string]int has string keys and int values.
The basics
platforms := map[string]int{"Bank": 3, "Oval": 1} platforms["Angel"] = 2 // add or replace fmt.Println(platforms["Bank"]) // 3 fmt.Println(len(platforms)) // 3 delete(platforms, "Oval") // PHP's unset()
Every key has the key type and every value has the value type, so there's no mixing 'Bank' => 3 with 'Bank' => 'three'. delete on a key that isn't there does nothing. range over a map gives you each key and value, though not in any order you can rely on. That's the whole of the next lesson.
Keys are typed too, which fixes an old PHP oddity. PHP quietly turns the key '1' into the integer 1, and true into 1 as well, so ['1' => 'a', true => 'c'] has just one element. In Go, a map[string]int key is a string, always. Any type that can be compared with == can be a key: strings, numbers, bools, arrays and simple structs. Slices can't, because they aren't comparable.
Missing keys
Read a key that isn't there and Go hands you the zero value of the value type, not null. platforms["Croydon"] is 0.
That's handy: counting is just counts[s]++, because a station you haven't seen yet starts at 0, with no ?? 0 needed. But it means a missing key and a key whose value is zero look the same. For that, a map lookup can return a second value:
p, ok := platforms["King's Cross"]
This is the comma-ok idiom you were promised back in 1.4 and 1.8:
flowchart TB accTitle: A comma-ok map lookup accDescr: The lookup p, ok := platforms[station] checks whether the key is in the map. If it is, p is the stored value and ok is true, even when the stored value is 0. If it isn't, p is the zero value, 0, and ok is false. look["p, ok := platforms[station]"] --> q["Is the key in the map?"] q -->|yes| yes["p = the stored value<br/>ok = true, even if p is 0"] q -->|no| no["p = 0, the zero value<br/>ok = false"]
Use ok whenever zero is a real value. King's Cross really does have a platform 0, so if p == 0 would announce that a major station doesn't exist. The exercise has that exact bug. You'll often see the lookup inside an if: if p, ok := platforms[s]; ok { … }.
The nil map
A map's zero value is nil, just like a slice's. A nil map is fine to read. Every lookup returns the zero value and len is 0. But writing to one panics:
Mind the gap
var counts map[string]int then counts["Bank"]++ panics with assignment to entry in nil map. A nil slice happily accepts append (2.2), so this catches people. Always make the map before writing to it: a literal, counts := map[string]int{}, or make(map[string]int). Struct fields that hold maps need making too, before anything writes to them.
Maps share, like slices
Assigning a map or passing it to a function shares it, just like a slice in 2.3. If a function adds a key, the caller sees it. When you need an independent copy, maps.Clone(m) makes one. And clear(m) empties a map in place.
Recap
map[K]Vis Go's associative array. Keys and values are typed, and keys must be comparable.- A missing key reads as the zero value.
v, ok := m[k]tells you whether it was really there. counts[k]++works without checking first, because missing keys start at zero.- A nil map can be read but not written. Make it with
map[K]V{}ormakefirst. - Maps are shared when assigned or passed.
maps.Clonecopies, anddeleteremoves a key.