How to iterate over and order a map in Go

You can iterate over maps in Go but, unlike slices, the order of iteration is not guaranteed. That’s because a map is explictly defined to be unordered in the Go Specification so the runtime deliberately randomises its iteration order to ensure that developers are aware of its unordered nature. Having said that, there are ways to get around this limitation, and that is what will be addressed in this article.

The idiomatic way to iterate over a map in Go is by using the for..range loop construct. Instead of receiving index/value pairs as with slices, you’ll get key/value pairs with maps. The iteration order is intentionally randomised when you use this technique.

func main() {
	m := map[string]int{
		"a": 1,
		"b": 2,
		"c": 3,
	}

	for key, value := range m {
		fmt.Println(key, "->", value)
	}
}
Output
b -> 2
c -> 3
a -> 1

To guarantee a specific iteration order, you need to create some additional data structures and use a method from the sort package to control the order of iteration.

func main() {
	m := map[string]int{
		"a": 1,
		"b": 2,
		"c": 3,
	}

	// Create a struct for each map key-value pair
	// Make sure the types match accordingly
	type KeyValue struct {
		Key   string
		Value int
	}

	// create an empty slice of key-value pairs
	s := make([]KeyValue, 0, len(m))
	// append all map keys-value pairs to the slice
	for k, v := range m {
		s = append(s, KeyValue{k, v})
	}
}

A KeyValue struct is used to hold the values for each map key-value pair. This struct is placed in a slice whose initial capacity is set to the length of the map in question. Now that we have a slice of KeyValue structs, we can use the SortStable() method from the sort package to sort the slice in any way we please. Once the slice is sorted accordingly, you can iterate over it to get the desired order.

Here’s an example that sorts the slice of structs by value so that the slice is ordered in such a way that the greater numbers come before the lesser ones.

func main() {
	m := map[string]int{
		"a": 1,
		"b": 2,
		"c": 3,
	}

	// Create a struct for each map key-value pair
	// Make sure the types match accordingly
	type KeyValue struct {
		Key   string
		Value int
	}

	// create an empty slice of key-value pairs
	s := make([]KeyValue, 0, len(m))
	// append all map keys-value pairs to the slice
	for k, v := range m {
		s = append(s, KeyValue{k, v})
	}

	// sort the slice of key-value pairs by value in descending order
	sort.SliceStable(s, func(i, j int) bool {
		return s[i].Value > s[j].Value
	})

	// iterate over the slice to get the desired order
	for _, v := range s {
		fmt.Println(v.Key, "->", v.Value)
	}
}
Output
c -> 3
b -> 2
a -> 1

If you want to sort the map by key, you only need to change the function argument to SliceStable() so it sorts by the struct’s Key instead of Value.

Conclusion

As you can see, it’s not impossible to sort a map in Go although it’s not the most convenient thing to do. Ensure to check out this article on sorting in Go in case you require a more complex sorting algorithm to achieve the desired order.

Thanks for reading, and happy coding!