# How to Collect Map Keys or Values Using `maps.Keys` and `slices.Collect` in Go 1.23

> Learn to collect Go map keys or values efficiently using maps.Keys and slices.Collect in Go 1.23. Avoid manual loops with these powerful standard library functions.

- Repository: [JetBrains/go-modern-guidelines](https://github.com/jetbrains/go-modern-guidelines)
- Tags: how-to-guide
- Published: 2026-08-29

---

**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:

```go
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.Collect` drains 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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/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:

```go
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.Keys` and `maps.Values`** to obtain lazy iterators over map contents without immediate allocation.
- **Pass iterators to `slices.Collect`** to 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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines_test.go).
- **Sort afterward** with `slices.Sort` if 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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines_test.go) and implemented in [`internal/guidelines/guidelines.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.go), which flags manual map-to-slice conversions in favor of the `maps` and `slices` utilities.