How to Collect Map Keys or Values Using `maps.Keys` and `slices.Collect` in Go 1.23
Use the standard library functions maps.Keys or maps.Values to obtain an iterator over a map, then pass that iterator to slices.Collect to produce a slice without manual looping.
Go 1.23 introduces first-class iterator support to the standard library, making it trivial to extract keys or values from any map. The JetBrains/go-modern-guidelines repository explicitly codifies this pattern, recommending it over legacy manual collection loops. By combining the maps and slices packages, you can write generic, allocation-efficient code that works with any map[K]V type.
How maps.Keys and slices.Collect Work Together
Lazy Iteration with maps.Keys and maps.Values
In Go 1.23, the maps package provides two generic functions that yield iterators rather than allocated slices. maps.Keys returns an iterator that visits every key in the map, while maps.Values does the same for values. Because these functions return iter.Seq[T], they perform no allocation until the iterator is consumed.
Materializing Results with slices.Collect
The slices.Collect function accepts any iterator (iter.Seq[E]) and appends each element to a new slice, returning []E. This separates the iteration logic from storage, letting you control when the slice is built. The combination eliminates the need for verbose make and append boilerplate inside for range loops.
Practical Code Example
The following example demonstrates collecting both keys and values from a map using idiomatic Go 1.23:
package main
import (
"fmt"
"maps"
"slices"
)
func main() {
settings := map[string]int{
"timeout": 30,
"retries": 3,
"workers": 10,
}
// Collect all keys into a slice
keys := slices.Collect(maps.Keys(settings))
fmt.Printf("Keys: %v\n", keys)
// Collect all values into a slice
values := slices.Collect(maps.Values(settings))
fmt.Printf("Values: %v\n", values)
}
Key behaviors demonstrated:
maps.Keys(settings)yields an iterator; no slice is created yet.slices.Collectdrains the iterator and returns[]string.- Map iteration order is random, so the output slice order is unspecified.
Enforcement in the JetBrains Guidelines
The JetBrains/go-modern-guidelines repository treats manual map iteration as an anti-pattern when the sole purpose is to build a slice. According to the test suite defined in internal/guidelines/guidelines_test.go, rules explicitly flag code that manually appends map elements inside a for loop. Instead, the guidelines promote the slices.Collect(maps.Keys(m)) idiom as the canonical solution. The rule logic is implemented in internal/guidelines/guidelines.go, ensuring that modern Go projects adopt these generic helpers consistently.
Sorting Collected Results
Because Go randomizes map iteration order, you often need deterministic output. After collection, use slices.Sort (or slices.SortFunc for custom types) to order the results:
keys := slices.Collect(maps.Keys(settings))
slices.Sort(keys) // Now keys are in lexical order
This two-step pattern—collect then sort—provides clarity and avoids modifying the map itself.
Summary
- Use
maps.Keysandmaps.Valuesto obtain lazy iterators over map contents without immediate allocation. - Pass iterators to
slices.Collectto materialize them into slices safely and generically. - Prefer this pattern over manual loops, as enforced by the JetBrains guidelines in
internal/guidelines/guidelines_test.go. - Sort afterward with
slices.Sortif you require a deterministic order.
Frequently Asked Questions
Do maps.Keys and maps.Values return a slice directly?
No. In Go 1.23, both functions return an iterator (iter.Seq[T]). You must pass this iterator to slices.Collect to produce a slice. This design avoids forcing allocations when you only need to traverse elements once.
Which packages do I import for this pattern in Go 1.23?
Import the standard library packages "maps" and "slices". These are part of the Go 1.23 standard library and do not require external dependencies like golang.org/x/exp.
Is the order of keys returned by maps.Keys deterministic?
No. The order is randomized, consistent with standard Go map iteration semantics. If you need a specific order, collect the keys and then sort the resulting slice using slices.Sort.
Where is this pattern officially recommended?
The pattern is formally recommended in the JetBrains/go-modern-guidelines repository. The specific rule is tested in internal/guidelines/guidelines_test.go and implemented in internal/guidelines/guidelines.go, which flags manual map-to-slice conversions in favor of the maps and slices utilities.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →