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.name instead of player["name"]. Maps have no dot notation.
  • Writing player[name] with no quotes. name without quotes is a variable, so Go looks for a variable called name, and stops with undefined: name if there is none.
  • Writing "level": 7 in this map. Go stops, because 7 is 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's range gives the key, not the value. weight would hold "sword", and total += weight would 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 counts directly to print. The lines would come out in a random order.
  • Checking ok before adding. It works, but it takes four lines for what counts[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.