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:
- Declaring and initializing maps
- Map types in Go
- Storing and retrieving map values
- Finding whether a Go map key exists
- Iterating over a Go map
map[string]interface{}
in Go- 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:
:= map[string]float64{
menu "eggs": 1.75,
"bacon": 3.22,
"sausage": 1.89,
}
.Println(menu) fmt
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:
.Println("Gopher's Diner Breakfast Menu")
fmtfor dish, price := range menu {
.Println(dish, price)
fmt}
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 {
.Printf("Remember to buy %s\n", dish)
fmt}
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 {
+= price
total }
.Printf("An 'everything breakfast' will cost you %.02f\n", total) fmt
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 {
= append(dishes, dish)
dishes }
.Strings(dishes)
sort.Println("In alphabetical order:")
fmtfor _, dish := range dishes {
.Println(dish, menu[dish])
fmt}
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