Go cookbook · Intermediate
Write small generic helpers with slices and maps
The same filter, map and group-by loops are rewritten for every type.
Approach
Use the standard slices and maps packages first (slices.SortFunc, slices.Contains, maps.Keys), and
write small generic functions with type parameters and constraints for what's missing.
Solution
package main
import (
"cmp"
"fmt"
"maps"
"slices"
)
func Filter[T any](items []T, keep func(T) bool) []T {
var out []T
for _, item := range items {
if keep(item) {
out = append(out, item)
}
}
return out
}
func GroupBy[T any, K comparable](items []T, key func(T) K) map[K][]T {
groups := make(map[K][]T)
for _, item := range items {
k := key(item)
groups[k] = append(groups[k], item)
}
return groups
}
func MaxBy[T any, V cmp.Ordered](items []T, value func(T) V) (T, bool) {
var best T
if len(items) == 0 {
return best, false
}
best = items[0]
for _, item := range items[1:] {
if value(item) > value(best) {
best = item
}
}
return best, true
}
type Order struct {
Customer string
Cents int
}
func main() {
orders := []Order{{"ana", 2400}, {"ben", 850}, {"ana", 1200}, {"cy", 3100}}
big := Filter(orders, func(o Order) bool { return o.Cents >= 1000 })
byCustomer := GroupBy(orders, func(o Order) string { return o.Customer })
top, _ := MaxBy(orders, func(o Order) int { return o.Cents })
slices.SortFunc(big, func(a, b Order) int { return cmp.Compare(b.Cents, a.Cents) })
customers := slices.Sorted(maps.Keys(byCustomer))
fmt.Println(big, customers, top)
}
Pitfalls
- Generic abstractions for one or two uses; plain code is often clearer.
- Using
anywhere a constraint (cmp.Ordered,comparable) documents and enforces requirements. - Re-implementing
slices.Index,slices.Containsormaps.Clone.
Variations
- Return iterators (
iter.Seq[T]) instead of slices for large or lazy sequences. slices.Collect(maps.Values(m))turns a map's values into a slice.
Further reading: slices package
Reference: https://pkg.go.dev/slices
More Go recipes
- Benchmark code with testing.B.Loop
- Build a small, secure container image for a Go service
- Decode and validate JSON request bodies safely
- Embed static files and templates in the binary
- Load and validate configuration from environment variables
- Log structured JSON with slog and redact secrets
- Process jobs concurrently with a bounded worker pool
- Profile a running service with pprof