Go cookbook · Intermediate

Write small generic helpers with slices and maps

The same filter, map and group-by loops are rewritten for every type.

Create a free account to save this page, take notes and track your progress. Sign in

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 any where a constraint (cmp.Ordered, comparable) documents and enforces requirements.
  • Re-implementing slices.Index, slices.Contains or maps.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

Was this page helpful?