Question
In Go, is there a simpler or more idiomatic way to get a slice of keys from a map?
Right now, I am iterating over the map and copying each key into a slice:
i := 0
keys := make([]int, len(mymap))
for k := range mymap {
keys[i] = k
i++
}
I want to know whether Go provides a cleaner built-in approach, or whether manually iterating over the map is the standard way to do this.
Short Answer
By the end of this page, you will understand how to extract keys from a map in Go, why iteration is the normal approach, and how to write this pattern clearly and safely. You will also learn when to preallocate a slice, when to use append, and how to sort keys when order matters.
Concept
In Go, a map is an unordered collection of key-value pairs. If you want all keys from a map, you must iterate over the map and collect them yourself.
There is no built-in function in the standard language syntax that directly returns a slice of keys for any map. The idiomatic approach is to use a for range loop.
Why this matters:
- Maps are used everywhere in Go for lookups, caching, grouping, and counting.
- Very often, you need the keys separately for display, sorting, or further processing.
- Understanding that map iteration is explicit helps you write predictable and readable Go code.
A key detail is that map iteration order is not guaranteed. That means the slice of keys you build may come out in a different order each time. If you need a stable order, you must sort the keys after collecting them.
There are two common ways to build the keys slice:
- Preallocate with
makeand fill by index - Start empty and use
append
Both are valid. In many cases, using append is a little cleaner and easier to read.
Mental Model
Think of a Go map like a box of labeled index cards.
- The label on each card is the key.
- The content written on the card is the value.
- If you want a list of all labels, you must pick up each card one by one and copy its label into a new list.
Go does not hand you a ready-made list of labels automatically. You create that list by walking through the box yourself.
Syntax and Examples
Basic pattern
The standard way to get keys from a map is:
keys := make([]int, 0, len(m))
for k := range m {
keys = append(keys, k)
}
Why this version is nice
make([]int, 0, len(m))creates an empty slice- It reserves enough capacity for all keys
appendkeeps the code simple
Example
package main
import "fmt"
func main() {
mymap := map[int]string{
10: "apple",
20: "banana",
30: "orange",
}
keys := make([]int, 0, len(mymap))
for k := range mymap {
keys = append(keys, k)
}
fmt.Println(keys)
}
Possible output:
[20 30 10]
The order may change between runs.
Alternative: fill by index
Your original approach is also valid:
i := 0
keys := make([]int, len(mymap))
for k := range mymap {
keys[i] = k
i++
}
This works because the slice already has the exact final length.
If you need sorted keys
package main
import (
"fmt"
"sort"
)
func main() {
mymap := map[int]string{
10: "apple",
20: "banana",
30: "orange",
}
keys := make([]int, 0, len(mymap))
for k := range mymap {
keys = append(keys, k)
}
sort.Ints(keys)
fmt.Println(keys)
}
Output:
[10 20 30]
This is the correct approach when order matters.
Step by Step Execution
Consider this code:
m := map[int]string{
5: "a",
2: "b",
9: "c",
}
keys := make([]int, 0, len(m))
for k := range m {
keys = append(keys, k)
}
Step by step:
mis created with 3 key-value pairs.len(m)is3.make([]int, 0, len(m))creates:- a slice of length
0 - a capacity of
3
- a slice of length
- The loop
for k := range mstarts iterating over the map. - On each iteration, Go gives one key from the map.
append(keys, k)adds that key to the slice.- After all iterations,
keyscontains all map keys.
A possible trace:
- First iteration:
k = 9→keys = [9] - Second iteration:
k = 5→keys = [9 5] - Third iteration:
k = 2→keys = [9 5 2]
Another run could produce a different order.
If you sort afterward:
sort.Ints(keys)
Then the final result becomes:
[2 5 9]
Real World Use Cases
Getting keys from a map is common in real Go programs.
1. Displaying available IDs
You may store records in a map and need to list all IDs:
users := map[int]string{
101: "Ava",
102: "Noah",
}
You can extract the keys to show which users exist.
2. Sorting configuration names
A service might load feature flags into a map:
flags := map[string]bool{
"betaUI": true,
"logging": false,
}
You collect the keys, sort them, and print them in a stable order.
3. Building API responses
Sometimes your internal data is stored as a map, but the API response needs a list of keys or IDs.
4. Validating allowed values
You might keep allowed values in a map for fast lookup, then later need the keys for an error message:
allowed := map[string]struct{}{
"json": {},
"xml": {},
}
If the user passes an invalid format, you can return the list of valid formats.
5. Data processing and reporting
When counting grouped values, the final report may require the group names as a slice:
counts := map[string]int{
"books": 12,
"games": 7,
"movies": 4,
}
Real Codebase Usage
In real Go codebases, developers usually use one of these patterns.
Pattern 1: Extract keys with append
This is the most readable general-purpose style:
keys := make([]string, 0, len(settings))
for k := range settings {
keys = append(keys, k)
}
Pattern 2: Sort immediately when order matters
This is very common in CLI tools, logs, tests, and API output:
keys := make([]string, 0, len(settings))
for k := range settings {
keys = append(keys, k)
}
sort.Strings(keys)
Pattern 3: Helper function
Teams often wrap this in a small helper for repeated use:
func mapKeys(m map[string]int) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
Pattern 4: Use for validation messages
Maps are often used as sets in Go. Then keys are extracted for user-facing output:
validRoles := map[string]struct{}{
"admin": {},
"editor": {},
"viewer": {},
}
When validation fails, developers gather and sort the keys to produce a clear message.
Pattern 5: Stable output in tests
Because map order is not stable, tests often fail if they compare unsorted map-derived slices. A common fix is:
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
This avoids flaky tests.
Common Mistakes
1. Expecting map keys to come out in a fixed order
Broken assumption:
m := map[int]string{1: "a", 2: "b", 3: "c"}
keys := make([]int, 0, len(m))
for k := range m {
keys = append(keys, k)
}
// Do not assume keys == [1, 2, 3]
Fix
Sort the slice if order matters.
sort.Ints(keys)
2. Mixing up slice length and capacity
Broken code:
keys := make([]int, 0, len(m))
for k := range m {
keys[i] = k // panic: index out of range
i++
}
Why it breaks:
- Length is
0, sokeys[0]is invalid - Capacity is reserved, but elements do not exist yet
Fix
Use append:
keys := make([]int, 0, len(m))
for k := range m {
keys = append(keys, k)
}
Or create the slice with full length if you want index assignment:
keys := make([]int, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
3. Forgetting to increment the index
Broken code:
i := 0
keys := make([]int, len(m))
for k := range m {
keys[i] = k
}
This keeps writing to the same position and will not work correctly.
Fix
Increment i each time, or switch to append.
4. Assuming Go has a universal built-in keys() for maps
Go's core map handling is explicit. You typically write the loop yourself or create a helper function.
5. Using unsorted keys in tests
Broken test idea:
want := []string{"a", "b", "c"}
If got comes from a map without sorting, the order can differ.
Fix
Sort both sides or sort the extracted keys before comparison.
Comparisons
Common ways to collect map keys
| Approach | Example | Pros | Cons | When to use |
|---|---|---|---|---|
| Preallocate full length and assign by index | keys := make([]int, len(m)) | Efficient, no append calls | Needs manual index variable | When you know exact length and are comfortable managing i |
Empty slice with capacity and append | keys := make([]int, 0, len(m)) | Readable, idiomatic, less error-prone | Slightly more abstract for beginners | Best default choice |
| Empty slice without capacity | var keys []int then append | Short and simple | May reallocate as it grows | Fine for small maps or quick code |
| Helper function | func keys(m map[string]int) []string | Reusable, clean call sites | Extra function to maintain | Useful when repeated often |
append vs index assignment
| Style | Example | Main idea |
|---|---|---|
append | keys = append(keys, k) | Grow the slice one element at a time |
| Index assignment | keys[i] = k | Fill an already-sized slice at known positions |
Ordered vs unordered results
| Source | Order guaranteed? |
|---|---|
| Iterating a Go map | No |
| Sorting keys after extraction | Yes |
If your program depends on order, extract first, then sort.
Cheat Sheet
Key pattern
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
Replace K with the map key type.
Example with int keys
keys := make([]int, 0, len(m))
for k := range m {
keys = append(keys, k)
}
Example with string keys
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
If order matters
sort.Ints(keys)
sort.Strings(keys)
Alternative indexed version
i := 0
keys := make([]int, len(m))
for k := range m {
keys[i] = k
i++
}
Rules to remember
- Go maps are unordered
- There is no built-in map-to-keys conversion in basic syntax
- Iteration is the normal way to collect keys
- Use
appendfor cleaner code in many cases - Sort keys if you need predictable output
Common trap
This is wrong:
keys := make([]int, 0, len(m))
keys[0] = 10
Why: length is 0, so index 0 does not exist yet.
FAQ
Is there a built-in Go function to get all map keys?
Not in the basic language syntax for ordinary maps. The standard approach is to iterate with for range and collect the keys.
What is the idiomatic way to get map keys in Go?
Usually:
keys := make([]KeyType, 0, len(m))
for k := range m {
keys = append(keys, k)
}
Why is my keys slice in a different order each run?
Because Go map iteration order is not guaranteed. Sort the keys if you need stable output.
Should I use append or assign by index?
Both work. append is often easier to read and less error-prone. Index assignment is fine if you pre-size the slice correctly.
Why use make([]T, 0, len(m)) instead of just var keys []T?
It preallocates enough capacity for all keys, which can reduce reallocations while keeping the code simple.
Can I get both keys and values during iteration?
Yes:
for k, v := range m {
fmt.Println(k, v)
}
Do I need to sort keys in tests?
Usually yes, if your expected result depends on order. Otherwise tests may be inconsistent.
Mini Project
Description
Build a small Go program that stores a set of product IDs in a map and then extracts the keys into a slice. This demonstrates the standard pattern for collecting map keys, and it also shows how to sort them for stable output.
Goal
Create a program that reads keys from a map, stores them in a slice, sorts the slice, and prints the result.
Requirements
- Create a map with at least five integer keys
- Extract all keys into a slice using a
for rangeloop - Use
appendto build the slice - Sort the keys before printing
- Print the final sorted slice
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.