23. Maps — Homework solutions
The .go solution files are in
exercises/23/homework/solutions/.
Problem 1 — Player record
Problem. Build a player map, and print each key and value in sorted order.
How to think about it. A literal with the four keys. Then the three steps from the chapter: collect the keys, sort them, loop over them.
Worked solution.
package main
import (
"fmt"
"slices"
)
func main() {
player := map[string]string{
"name": "Keiko",
"class": "Mage",
"level": "7",
"alive": "true",
}
keys := []string{}
for key := range player {
keys = append(keys, key)
}
slices.Sort(keys)
for _, key := range keys {
fmt.Println(key, player[key])
}
}Output:
alive true
class Mage
level 7
name Keiko
Without the sort, the lines would come out in a different order from run to run.
Common mistakes.
- Writing
player.nameinstead ofplayer["name"]. Maps have no dot notation. - Writing
player[name]with no quotes.namewithout quotes is a variable, so Go looks for a variable calledname, and stops withundefined: nameif there is none. - Writing
"level": 7in this map. Go stops, because7is not a string.
Problem 2 — Inventory weights
Problem. Add up the values in a map.
How to think about it. Keep a running total, as in
Chapter 22. The key is not needed, so it is _. The order
does not matter for a sum, so no sorting is needed.
Worked solution.
package main
import "fmt"
func main() {
inventory := map[string]float64{
"sword": 3.5,
"shield": 5.0,
"potion": 0.2,
"map": 0.1,
"coins": 0.05,
}
total := 0.0
for _, weight := range inventory {
total += weight
}
fmt.Printf("Total weight: %.2f\n", total)
}Output:
Total weight: 8.85
total := 0.0 makes a float64, to match the values. With
total := 0, it would be an int, and
total += weight would stop with a
mismatched types error.
Common mistakes.
- Writing
for weight := range inventory. With one variable, a map'srangegives the key, not the value.weightwould hold"sword", andtotal += weightwould not compile.
Problem 3 — Safe lookup
Problem. lookup returns the value or
"(not found)".
How to think about it. Ask for two values, and check
ok. Return the placeholder when it is false. Otherwise
return the value.
Worked solution.
package main
import "fmt"
func lookup(player map[string]string, key string) string {
value, ok := player[key]
if !ok {
return "(not found)"
}
return value
}
func main() {
player := map[string]string{"name": "Keiko", "class": "Mage"}
fmt.Println(lookup(player, "name")) // Keiko
fmt.Println(lookup(player, "email")) // (not found)
key := "class"
fmt.Println(lookup(player, key)) // Mage
}Common mistakes.
- Testing the value instead of
ok:
func lookup(player map[string]string, key string) string {
if player[key] == "" {
return "(not found)"
}
return player[key]
}A missing key gives "", so this seems to work. But a key
that is there and really holds "" is reported as not found.
ok is the only thing that tells them apart.
Challenge — Word frequency
Problem. Count occurrences of each word in a slice.
How to think about it. Walk the slice once. For each
word, add 1 to its count. The first time a word is seen, it is not in
the map yet, and a missing key gives 0. So 0
plus 1 gives 1, and no special case is needed:
counts := map[string]int{}
for _, word := range words {
counts[word]++
}Then sort the keys for printing.
Worked solution.
package main
import (
"fmt"
"slices"
)
func main() {
words := []string{"apple", "banana", "apple", "cherry", "apple", "banana"}
counts := map[string]int{}
for _, word := range words {
counts[word]++
}
keys := []string{}
for word := range counts {
keys = append(keys, word)
}
slices.Sort(keys)
for _, word := range keys {
fmt.Println(word, counts[word])
}
}Output:
apple 3
banana 2
cherry 1
Common mistakes.
- Looping over
countsdirectly to print. The lines would come out in a random order. - Checking
okbefore adding. It works, but it takes four lines for whatcounts[word]++does in one, because the zero value is already the right start.
Done?
Next, Working with slices gives you ready-made tools for sorting, reversing and searching, and the last chapter of Part 5 splits code across files using packages. After that, the Part 5 mini-project, a Text Adventure, combines maps, slices, functions and packages into a tiny game world.