How to iterate over a map in Go
for key, value := range m — that is the entire loop. The catch is that Go randomises map iteration order on purpose, so anything you print inside that loop comes out in a different order on every run. When the order matters, range the sorted keys instead: for _, k := range slices.Sorted(maps.Keys(m)) on Go 1.23+.
A map is a hash table, and Go goes out of its way to make that visible: the runtime starts each range at a random position, so two loops over the same unmodified map can hand you the entries in two different orders. That is a feature — it stops code from quietly depending on an order the language never promised — but it means "iterate over a map" is really two questions: how do you visit every entry, and how do you visit them in an order you can rely on. This page answers both, then covers what you may safely change while the loop is running. Every example runs on this page — hit Run, then edit the code and run it again.
1for k, v := range mRecommended
One range statement gives you the key and the value of every entry, exactly once each. There is no iterator to create, nothing to close, and no index — the two loop variables are the key and a copy of the value. The only thing to be careful about is what you do with the results: an aggregate is order-independent, but printed output is not, so collect and sort before you print.
Output
Prints dns -> 53, http -> 80, https -> 443, ssh -> 22, then entries: 4 total: 598 — and it prints that on every run only because of the slices.Sort. Two details worth knowing: value is a copy, so assigning to it changes nothing (write m[k] = v back if you meant to update the map), and a range over a nil map is legal and simply runs zero iterations, so you never need a nil check before the loop.
2Keys only, values only
Drop the second variable and range yields keys alone — for k := range m — which also skips copying each value, a real saving when the values are big structs. Blank the key with _ for the mirror image. Go 1.23 added maps.Keys and maps.Values, which return iterators rather than slices, so they pair with slices.Sorted or slices.Collect.
Output
Prints [apple banana cherry], units: 19 kinds: 3, then [apple banana cherry] and [0 7 12]. Note that len(m) already counts entries — never range a map just to count it. And maps.Keys(m) is not the old golang.org/x/exp/maps.Keys, which returned a []K; the standard library version returns an iter.Seq[K], so wrap it in slices.Collect if you want the unordered slice, or slices.Sorted if you want it sorted.
3Why the order is random on purpose
The Go spec says the iteration order over maps "is not specified", and since Go 1.0 the runtime has actively randomised it: each range picks a random starting bucket and a random offset inside it. Early Go programs were accidentally depending on the incidental order of a particular implementation, so the team made the disorder loud enough to catch in development instead of in production. This snippet ranges the same four-entry map a thousand times and reports what it saw.
Output
Both questions answer true, and the third line is first keys seen: [alice bob carol dana] — every key led at least once in a thousand loops. The map was never modified between iterations; the order still moved. Note the shape of the assertions: the snippet prints booleans and a sorted list rather than the raw orders, because printing the orders themselves would produce different output every run. That is the discipline this whole page is about.
4Iterating in a deterministic order
You cannot sort a map — it has no order to change. What you sort is a slice of its keys, and then you range that slice and look each value up. On Go 1.23+ the whole collect-and-sort step is a single expression, slices.Sorted(maps.Keys(m)). Ordering by value is the same idiom with a comparison that dereferences the map, plus a tie-breaker so that equal values still come out in a fixed order.
Output
Prints go 98, rust 124, zig 35, then [go rust zig], and finally [rust go zig] — the same three keys reordered by vote count. The cmp.Or tie-breaker is not decoration: without it, two keys with equal values would be ordered by an unstable sort over a slice that arrived in random order, and your output would flicker again. Pre-sizing with make([]string, 0, len(votes)) avoids regrowing the slice, and the sort is O(n log n) each time — cache the sorted key slice if you iterate the same unchanged map in a loop. More ways to order the key slice are in sort a slice.
5Which form should you use?
| Form | Order | Best for |
|---|---|---|
| for k, v := range m | Randomised | The default — aggregates, side effects, building output |
| for k := range m | Randomised | Keys only; skips copying each value |
| for _, v := range m | Randomised | Summing or scanning values, keys irrelevant |
| slices.Sorted(maps.Keys(m)) | Sorted | Go 1.23+ — printing, tests, golden files |
| collect keys + slices.Sort | Sorted | Any Go version; the portable idiom |
| slices.SortFunc on the key slice | Custom | Ordering by value, or by a computed key |
6Deleting and adding during a range
Unlike most languages, Go states exactly what happens here. Deleting is safe: if you remove an entry the loop has not reached yet, it is simply never produced. Adding is legal but unpredictable: a new entry "may be produced during the iteration or may be skipped", and that choice can differ from run to run. So delete freely, but if you need to insert, snapshot the keys first and mutate afterwards.
Output
Prints [alice carol], then 0 false, then [a a2 b b2]. The delete-while-ranging filter is deterministic because the decision for each entry depends only on that entry. clear(m) empties a map without replacing it, which is why the second line reports length 0 but scores == nil is false — every other holder of that map sees the same empty map. The last loop ranges a slice of keys, so the four entries it ends up with never depend on iteration timing. One thing that is never safe: writing to a map from another goroutine while this one ranges it — the runtime detects that and panics with concurrent map iteration and map write.
7Nested maps and map[string][]T
Two shapes cover most real data: a map whose values are slices, and a map of maps. For a map[string][]T the inner range is over a slice, which does have an order, so only the outer loop needs sorting. For a map of maps you have to sort at every level, or the nesting reintroduces the randomness one layer down.
Output
Prints go: gofmt, go vet, rust: clippy, [zig fmt] 3, then the four eu/de = 3 … us/ny = 8 lines in sorted order. The asymmetry in the middle is the one to remember: append(tools["zig"], …) works on a missing key because reading a map gives the zero value and appending to a nil slice allocates, but the same move on a map of maps panics — sales["apac"]["syd"] = 1 assigns into a nil inner map. Create the inner map first, guarding with a comma-ok lookup.
Frequently asked questions
Why is Go map iteration order random?
Because the specification says the order is not specified, and the runtime enforces that by starting every range at a random bucket and a random offset within it. Early Go code was accidentally relying on the order a particular implementation happened to produce, so the randomisation was added to break that dependency loudly during development rather than silently in production. The order can differ between two loops over the same map in the same program.
How do I iterate over a map in sorted key order in Go?
Collect the keys into a slice, sort the slice, then range the slice and look each value up with m[k]. On Go 1.23 and later the collect-and-sort is one expression: for _, k := range slices.Sorted(maps.Keys(m)). There is no way to sort the map itself — a map has no order to change.
Can I delete from a map while ranging over it?
Yes. The spec explicitly allows it: an entry removed before the loop reaches it is simply never produced, and the loop still terminates. Adding entries during a range is also legal, but a new entry may or may not be produced by that same loop, so the result is unpredictable — snapshot the keys with slices.Sorted(maps.Keys(m)) and insert after the loop instead.
How do I sort a map by value in Go?
Collect the keys, then sort that slice with a comparison that looks up the values: slices.SortFunc(keys, func(a, b string) int { return cmp.Compare(m[b], m[a]) }) for descending. Wrap it in cmp.Or with a comparison of the keys themselves so ties come out in a fixed order — without a tie-breaker an unstable sort over randomly ordered keys can still vary between runs.
Does for k, v := range m copy each value?
Yes — v is a copy of the value, so assigning to v does not change the map; write m[k] = v back if you meant to update it. Use for k := range m when you only need keys, which avoids the copy entirely, and store *T or index by key when the values are large structs. You cannot take the address of a map element (&m[k] is a compile error), which is exactly why.
Is it safe to range a map from multiple goroutines?
Concurrent reads alone are safe, but a write from any goroutine while another is ranging is not: the runtime detects it and panics with concurrent map iteration and map write. Guard the map with a sync.RWMutex, or use sync.Map when the access pattern is many readers with disjoint writers.