Iterating over a Golang map

Iterating over a Golang map

See also Iterators in Go for information about the new “range over func” experiment.

This is part 5 of a series on Go maps:

  1. Declaring and initializing maps
  2. Map types in Go
  3. Storing and retrieving map values
  4. Finding whether a Go map key exists
  5. Iterating over a Go map
  6. map[string]interface{} in Go
  7. Frequently asked questions about Go maps

How do you iterate over Golang maps? How do you print a map? How do you write a for loop that executes for each key and value in a map? What is the iteration order of Go maps? In Part 4 of this series, we saw how to query a specific key in a map, but how can you get all the keys?

Since a Go map is fundamentally a collection of things, it makes sense that we would want to use it with for loops, where we do the same operation with every key-value pair in the map.

How to print a Golang map

Before we look at loops, let’s consider one of the simplest things we might want to do with a map in Go: print it out! That’s easy, as you might expect; we can use the all-purpose fmt library:

menu := map[string]float64{
    "eggs":    1.75,
    "bacon":   3.22,
    "sausage": 1.89,
}
fmt.Println(menu)

The result:

map[bacon:3.22 eggs:1.75 sausage:1.89]

This is a quick way to see the contents of a map, especially if you’re trying to debug a program, but it’s not a particularly delightful format, and we have no control over it. For more flexible printing, we can iterate over the map.

How to iterate over a map

To iterate over a map is to read each successive key-value pair in a loop. We can do this using the range operator. For example, to print out our diner menu we might write:

fmt.Println("Gopher's Diner Breakfast Menu")
for dish, price := range menu {
    fmt.Println(dish, price)
}

range on a map returns two values (received as the variables dish and price in our example), which are the key and value respectively. Each time round the loop, dish is set to the next key, and price is set to the corresponding value.

The results:

Gopher's Diner Breakfast Menu
eggs 1.75
bacon 3.22
sausage 1.89

Delicious!

Listing the keys in a map

Suppose we weren’t bothered about the prices for the moment, and we simply wanted to make a list of dishes, perhaps for when we go shopping for ingredients. When we don’t want the second value returned by range, we simply don’t supply a variable to receive it:

for dish := range menu {
    fmt.Printf("Remember to buy %s\n", dish)
}

Result:

Remember to buy eggs
Remember to buy bacon
Remember to buy sausage

Getting only the values in a map

When we only want the second value from range, we can supply a placeholder to receive the first value: the blank identifier_”:

// Just give me all the bacon and eggs you have.
var total float64
for _, price := range menu {
    total += price
}
fmt.Printf("An 'everything breakfast' will cost you %.02f\n", total)

Result:

An 'everything breakfast' will cost you 6.86

Map iteration order

An interesting question to ask about maps in Go is “what is the iteration order of a map?” In other words, when we loop over successive keys with a for statement, what does ‘successive’ mean?

You might think that the iteration order of a map is simply the order that you defined the keys in. For example, we listed eggs first in the map literal, so shouldn’t we see eggs as the first key when we loop over it?

In fact, the Go spec says that the iteration order over maps is not specified. That is to say, you should not expect the map keys to appear in any particular order.

This makes sense when you understand how Go maps are implemented, but we needn’t worry about the details here. Just know that you can’t rely on iterating over a map in a specific order.

The iteration order of maps is also unstable, meaning that it won’t necessarily be the same every time you run the program. Beware of writing tests which depend on map keys appearing in a certain order: this is a flaky test which can pass one time, and fail the next!

Cool fact: when you print a map in Go, it’s always in key order. This is to make testing easier, but don’t rely on this. You shouldn’t think of the map as having any inherent ordering.

Ordering map keys

So how do you iterate over map keys in a certain order? The only way is to store the keys yourself, as a slice, and then order them the way you want:

var dishes []string
for dish := range menu {
    dishes = append(dishes, dish)
}
sort.Strings(dishes)
fmt.Println("In alphabetical order:")
for _, dish := range dishes {
    fmt.Println(dish, menu[dish])
}

The result:

In alphabetical order:
bacon 3.22
eggs 1.75
sausage 1.89

Next

Assuming you’re not too hungry to continue, you can go on to the next tutorial in this series, which is all about the very useful map[string]interface{} type.

Gopher image by egonelbre

Read more

map[string]interface{} in Go

map[string]interface{} in Go

Finding whether a Go map key exists

Finding whether a Go map key exists

0