Map and Filter with Generics and Go 1.23 Iterators

Lake with Clouds

I found myself missing map and filter constructs for some small things in Go, and here we are. Go 1.18 got generics, and I’ve admittedly not used them too much in real apps. Go 1.23 brought iterators.

The combo of these two makes map/filter pretty easy to create. They play nicely with the utilities in slices and maps.

This code is available on github with some tests.

Map and Map2

To map over an iter.Seq and iter.Seq2 respectively. Both of these take and return an iterator. Both range over the input iterator, apply a function, and call the yield callback with the resulting value.


package mapfilter

import (
	"iter"
)

func Map[V, U any](seq iter.Seq[V], f func(V) U) iter.Seq[U] {
	return func(yield func(U) bool) {
		for v := range seq {
			if !yield(f(v)) {
				break
			}
		}
	}
}

func Map2[K, V, O, U any](seq iter.Seq2[K, V], f func(K, V) (O, U)) iter.Seq2[O, U] {
	return func(yield func(O, U) bool) {
		for k, v := range seq {
			if !yield(f(k, v)) {
				break
			}
		}
	}
}

Filter and Filter2

For filtering an iter.Seq and iter.Seq2 respectively. Both take and return an iterator. Both range over the input iterator, check the value with a filter function, then call the yield callback if the filter passes.


package mapfilter

import (
	"iter"
)

func Filter[V any](seq iter.Seq[V], check func(V) bool) iter.Seq[V] {
	return func(yield func(V) bool) {
		for v := range seq {
			if !check(v) {
				continue
			}

			if !yield(v) {
				break
			}
		}
	}
}

func Filter2[K, V any](seq iter.Seq2[K, V], check func(K, V) bool) iter.Seq2[K, V] {
	return func(yield func(K, V) bool) {
		for k, v := range seq {
			if !check(k, v) {
				continue
			}

			if !yield(k, v) {
				break
			}
		}
	}
}
Posted in Go