iters

package module
v0.0.0-...-795446c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2025 License: MIT Imports: 4 Imported by: 0

README

iters

Go Reference

A collection of functions for working with iterators in Go — exploring a "comprehensive streams API" that's currently missing from the standard library (or any official experimental package so far).

iters.Map

The iters.Map function allows you to transform each element in a slice using a provided function, returning a new iterator with the transformed values. This is useful for applying a function to each element in a slice without modifying the original slice.

numbers := []int{1, 2, 3, 4, 5}

stringNumbers := iters.Map(
    slices.Values(numbers),
    func(n int) string {
        return fmt.Sprintf("%d", n)
    },
)

fmt.Println(slices.Values(stringNumbers))
// Output:
// ["1" "2" "3" "4" "5"]
numbers := []int{1, 2, 3, 4, 5}

squaredNumbers := iters.Map(
    slices.Values(numbers),
    func(n int) int {
        return n * n
    },
)

fmt.Println(slices.Values(squaredNumbers))
// Output:
// [1 4 9 16 25]
type Animal struct {
    Name string
    Legs int
}

animals := []Animal{
    {"cat", 4},
    {"dog", 4},
    {"fish", 0},
    {"bird", 2},
}

animalNames := iters.Map(
    slices.Values(animals),
    func(animal Animal) string {
        return animal.Name
    },
)

result := slices.Collect(animalNames)

fmt.Println(result)
// Output:
// [cat dog fish bird]

iters.Filter

The iters.Filter function allows you to filter elements in a slice based on a provided predicate function. It returns a new iterator containing only the elements that satisfy the predicate condition (i.e., the function returns true for that element). This is useful for creating a new slice that only contains elements that meet certain criteria, without modifying the original slice.

numbers := []int{1, 2, 3, 4, 5}

evenNumbers := iters.Filter(
    slices.Values(numbers),
    func(n int) bool {
        return n%2 == 0
    },
)

fmt.Println(slices.Values(evenNumbers))
// Output:
// [2 4]
animals := []Animal{
	{"cat", 4},
	{"dog", 4},
	{"fish", 0},
	{"bird", 2},
}

filteredAnimals := iters.Filter(
	slices.Values(animals),
	func(animal Animal) bool {
		return animal.Legs > 2
	},
)

result := slices.Collect(filteredAnimals)

fmt.Println(result)
// Output:
// [{cat 4} {dog 4}]

iters.Reduce

The iters.Reduce function allows you to reduce a slice to a single value by applying a provided function to each element in the slice. The function takes an accumulator value and the current element, and returns a new accumulator value. This is useful for aggregating values, such as summing numbers, concatenating strings, or performing any other kind of aggregation where you want to combine all elements in a slice into a single result.

type Animal struct {
    Name string
    Legs int
}

animals := []Animal{
    {"cat", 4},
    {"dog", 4},
    {"fish", 0},
    {"bird", 2},
}

totalLegs := iters.Reduce(
    slices.Values(animals),
    0,
    func(acc int, animal Animal) int {
        return acc + animal.Legs
    },
)

fmt.Println(totalLegs)
// Output:
// 10

iters.Contains

The iters.Contains function checks if a specific (comparable) value exists within a iter.Seq. It returns true if the value is found, and false otherwise.

numbers := []int{1, 2, 3, 4, 5}

hasThree := iters.Contains(
    slices.Values(numbers),
    3,
)
fmt.Println(hasThree)
// Output:
// true 

Documentation

Overview

Package iters provides building blocks for composing iter.Seq and iter.Seq2 pipelines.

Example (Simple_map)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	for v := range iters.Map(
		slices.Values(
			[]int{1, 2, 3, 4},
		),
		func(i int) string {
			return fmt.Sprintf("%d ", i)
		},
	) {
		fmt.Print(v)
	}

}
Output:

1 2 3 4
Example (Simple_map2)
package main

import (
	"fmt"
	"maps"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	// Use Map2 to transform the input map into a new map
	result := maps.Collect(
		iters.Map2(
			maps.All(
				map[string]int{"a": 1, "b": 2, "c": 3},
			),
			func(k string, v int) (string, int) {
				return k + "_new", v * 10
			},
		),
	)

	// For stable output, sort the keys of the resulting map
	// so we can iterate over the result map in a stable order.
	resultKeys := slices.Collect(maps.Keys(result))
	slices.Sort(resultKeys)

	// Collect and print the results in sorted order
	for _, k := range resultKeys {
		// Print each key-value pair in the new map
		fmt.Printf("%s: %d ", k, result[k])
	}

}
Output:

a_new: 10 b_new: 20 c_new: 30

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func After

func After[T any](seq iter.Seq[T], n int) iter.Seq[T]

After returns a sequence that discards the first n elements from seq before yielding the remainder. If n is zero or negative the original sequence is yielded unchanged. If n is larger than the number of elements the resulting sequence is empty.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	numbers := []int{1, 2, 3, 4, 5}

	afterTwo := iters.After(slices.Values(numbers), 2)

	fmt.Println(slices.Collect(afterTwo))
}
Output:

[3 4 5]

func AfterFunc

func AfterFunc[T any](seq iter.Seq[T], pred Predicate[T]) iter.Seq[T]

AfterFunc returns a sequence that drops elements from seq while pred reports true. The first element for which pred returns false and all subsequent elements are yielded. If pred never returns false, nothing is produced.

Example (Threshold)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	numbers := []int{1, 3, 2, 4, 5}

	afterSmall := iters.AfterFunc(
		slices.Values(numbers),
		func(v int) bool { return v < 3 },
	)

	fmt.Println(slices.Collect(afterSmall))
}
Output:

[3 2 4 5]

func Average

func Average[T Number](seq iter.Seq[T]) float64

Average returns the arithmetic mean of seq. It returns 0 when seq contains no values.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	values := []int{2, 4, 6, 8}

	fmt.Println(iters.Average(slices.Values(values)))
}
Output:

5

func AverageFunc

func AverageFunc[T any](seq iter.Seq[T], fn func(T) float64) float64

AverageFunc computes the arithmetic mean of fn(item) for every element in seq. It returns 0 when seq is empty.

Example (Lengths)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	words := []string{"go", "iters"}

	fmt.Println(
		iters.AverageFunc(
			slices.Values(words),
			func(s string) float64 { return float64(len(s)) },
		),
	)
}
Output:

3.5

func Before

func Before[T any](seq iter.Seq[T], n int) iter.Seq[T]

Before returns a sequence that yields at most n elements from seq. If seq is shorter than n the entire input is passed through.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	numbers := []int{1, 2, 3, 4, 5}

	beforeThree := iters.Before(slices.Values(numbers), 3)

	fmt.Println(slices.Collect(beforeThree))
}
Output:

[1 2 3]

func BeforeFunc

func BeforeFunc[T any](seq iter.Seq[T], pred Predicate[T]) iter.Seq[T]

BeforeFunc returns a sequence that yields elements from seq until pred first returns true. The matching element is not forwarded. If pred never returns true the entire input is yielded.

Example (UntilEven)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	numbers := []int{1, 3, 5, 4, 7}

	beforeEven := iters.BeforeFunc(
		slices.Values(numbers),
		func(v int) bool { return v%2 == 0 },
	)

	fmt.Println(slices.Collect(beforeEven))
}
Output:

[1 3 5]

func Chunk

func Chunk[T any](seq iter.Seq[T], size int) iter.Seq[[]T]

Chunk groups seq into contiguous slices of length size and returns a new sequence that yields those slices in order. The final chunk may be shorter if the input length is not divisible by size. When size <= 0 no chunks are produced.

Example (KeyValuePairs)
package main

import (
	"fmt"
	"maps"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	dictionary := map[string]string{
		"apple":  "A fruit",
		"banana": "Another fruit",
		"carrot": "A vegetable",
		"date":   "A sweet fruit",
		"egg":    "A protein source",
	}

	chunkSize := 2

	keys := slices.Collect(maps.Keys(dictionary))
	slices.Sort(keys)
	chunked := iters.Chunk2(
		func(yield func(string, string) bool) {
			for _, key := range keys {
				if !yield(key, dictionary[key]) {
					return
				}
			}
		},
		chunkSize,
	)

	resultKeys := [][]string{}
	resultValues := [][]string{}
	for keys, values := range chunked {
		resultKeys = append(resultKeys, keys)
		resultValues = append(resultValues, values)
	}

	fmt.Printf("%q\n", resultKeys)
	fmt.Printf("%q\n", resultValues)
}
Output:

[["apple" "banana"] ["carrot" "date"] ["egg"]]
[["A fruit" "Another fruit"] ["A vegetable" "A sweet fruit"] ["A protein source"]]
Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	numbers := []int{1, 2, 3, 4, 5, 6, 7}

	chunkSize := 3

	chunked := iters.Chunk(
		slices.Values(numbers),
		chunkSize,
	)

	result := slices.Collect(chunked)

	fmt.Println(result)
}
Output:

[[1 2 3] [4 5 6] [7]]

func Chunk2

func Chunk2[K, V any](seq2 iter.Seq2[K, V], size int) iter.Seq2[[]K, []V]

Chunk2 is the keyed companion to Chunk; it groups seq2 into slices of keys and values with the requested size. The final chunk may be shorter. When size <= 0 no chunks are produced.

func ChunkFunc

func ChunkFunc[T any](seq iter.Seq[T], pred Predicate[T]) iter.Seq[[]T]

ChunkFunc groups seq into slices, starting a new chunk each time pred returns true for an element. The matching element begins the next chunk. Any partial chunk is yielded when seq is exhausted.

Example (SplitOnZero)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	values := []int{1, 2, 0, 3, 4, 0, 5}

	chunks := iters.ChunkFunc(
		slices.Values(values),
		func(v int) bool { return v == 0 },
	)

	fmt.Println(slices.Collect(chunks))
}
Output:

[[1 2] [0 3 4] [0 5]]

func ChunkFunc2

func ChunkFunc2[K, V any](seq2 iter.Seq2[K, V], pred Predicate2[K, V]) iter.Seq2[[]K, []V]

ChunkFunc2 is the keyed companion to ChunkFunc; it starts a fresh chunk when pred reports true for a key/value pair. The matching pair becomes the first element of the next chunk.

Example (SplitPairs)
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	seq2 := func(yield func(string, int) bool) {
		data := []struct {
			Key string
			Val int
		}{
			{"a", 1},
			{"b", 2},
			{"b", 3},
			{"c", 4},
		}
		for _, item := range data {
			if !yield(item.Key, item.Val) {
				return
			}
		}
	}

	chunks := iters.ChunkFunc2(
		seq2,
		func(k string, _ int) bool { return k == "b" },
	)

	for keys, values := range chunks {
		fmt.Printf("%q %v\n", keys, values)
	}
}
Output:

["a"] [1]
["b"] [2]
["b" "c"] [3 4]

func CollectErr

func CollectErr[T any](seq iter.Seq2[T, error]) ([]T, error)

CollectErr gathers values from seq until a non-nil error occurs. It returns the values seen so far along with the first error, or nil if seq completed.

Example (Paginated)
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	type page struct {
		items []int
		err   error
	}
	pages := []page{
		{items: []int{1, 2}},
		{items: []int{3}},
	}

	seq := func(yield func(int, error) bool) {
		for _, p := range pages {
			for _, item := range p.items {
				if !yield(item, nil) {
					return
				}
			}
		}
	}

	values, err := iters.CollectErr(seq)
	fmt.Println(values, err)
}
Output:

[1 2 3] <nil>

func Compact

func Compact[T comparable](seq iter.Seq[T]) iter.Seq[T]

Compact collapses consecutive duplicate elements in seq, yielding the first element of each run.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	seq := iters.Compact(slices.Values([]int{1, 1, 2, 2, 3, 1}))
	fmt.Println(slices.Collect(seq))
}
Output:

[1 2 3 1]

func Compact2

func Compact2[K comparable, V comparable](seq2 iter.Seq2[K, V]) iter.Seq2[K, V]

Compact2 collapses consecutive duplicate key/value pairs in seq2, yielding the first pair from each run.

Example (Pairs)
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	seq2 := func(yield func(string, int) bool) {
		pairs := []struct {
			key string
			val int
		}{
			{"a", 1},
			{"a", 1},
			{"b", 2},
			{"b", 2},
		}
		for _, p := range pairs {
			if !yield(p.key, p.val) {
				return
			}
		}
	}

	var keys []string
	var values []int
	for k, v := range iters.Compact2(seq2) {
		keys = append(keys, k)
		values = append(values, v)
	}

	fmt.Println(keys)
	fmt.Println(values)
}
Output:

[a b]
[1 2]

func CompactFunc

func CompactFunc[T any](seq iter.Seq[T], equal func(a, b T) bool) iter.Seq[T]

CompactFunc collapses consecutive elements in seq for which equal reports true, yielding only the first element of each run.

Example (CaseInsensitive)
package main

import (
	"fmt"
	"slices"
	"strings"

	"github.com/picatz/iters"
)

func main() {
	seq := iters.CompactFunc(
		slices.Values([]string{"Go", "go", "iters", "ITERS"}),
		func(a, b string) bool { return strings.EqualFold(a, b) },
	)
	fmt.Println(slices.Collect(seq))
}
Output:

[Go iters]

func CompactFunc2

func CompactFunc2[K, V any](seq2 iter.Seq2[K, V], equal func(aK K, aV V, bK K, bV V) bool) iter.Seq2[K, V]

CompactFunc2 collapses consecutive key/value pairs in seq2 for which equal reports true, yielding the first pair of each run.

Example (Pairs)
package main

import (
	"fmt"
	"strings"

	"github.com/picatz/iters"
)

func main() {
	seq2 := func(yield func(string, string) bool) {
		pairs := []struct {
			key string
			val string
		}{
			{"a", "One"},
			{"a", "one"},
			{"b", "Two"},
		}
		for _, p := range pairs {
			if !yield(p.key, p.val) {
				return
			}
		}
	}

	var keys []string
	var values []string
	for k, v := range iters.CompactFunc2(
		seq2,
		func(k1, v1, k2, v2 string) bool {
			return strings.EqualFold(k1, k2) && strings.EqualFold(v1, v2)
		},
	) {
		keys = append(keys, k)
		values = append(values, v)
	}

	fmt.Println(keys)
	fmt.Println(values)
}
Output:

[a b]
[One Two]

func Compare

func Compare[T cmp.Ordered](s1, s2 iter.Seq[T]) int

Compare returns -1, 0, or +1 depending on the lexicographic ordering of s1 and s2, matching slices.Compare semantics. Elements are compared with cmp.Compare until a difference is found or one sequence ends.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	a := slices.Values([]int{1, 2, 3})
	b := slices.Values([]int{1, 2, 4})

	fmt.Println(iters.Compare(a, b))
}
Output:

-1

func CompareFunc

func CompareFunc[T1, T2 any](s1 iter.Seq[T1], s2 iter.Seq[T2], cmp func(T1, T2) int) int

CompareFunc behaves like Compare but calls cmp for each pair of elements. The first non-zero result is returned, otherwise the shorter sequence sorts before the longer one.

Example (IgnoreCase)
package main

import (
	"fmt"
	"slices"
	"strings"

	"github.com/picatz/iters"
)

func main() {
	a := slices.Values([]string{"Go", "iters"})
	b := slices.Values([]string{"go", "Iters"})

	fmt.Println(
		iters.CompareFunc(
			a,
			b,
			func(x, y string) int {
				xLower := strings.ToLower(x)
				yLower := strings.ToLower(y)
				switch {
				case xLower < yLower:
					return -1
				case xLower > yLower:
					return 1
				default:
					return 0
				}
			},
		),
	)
}
Output:

0

func Concat

func Concat[T any](seqs ...iter.Seq[T]) iter.Seq[T]

Concat returns a sequence that yields every element from seqs in order. Iteration stops early if the consumer stops pulling values.

Example (KeyValuePairs)
package main

import (
	"fmt"
	"maps"

	"github.com/picatz/iters"
)

func main() {
	map1 := map[string]int{
		"one": 1,
		"two": 2,
	}
	map2 := map[string]int{
		"three": 3,
		"four":  4,
	}

	concatenated := iters.Concat2(
		maps.All(map1),
		maps.All(map2),
	)

	result := maps.Collect(concatenated)

	fmt.Println(result["one"], result["two"], result["three"], result["four"])
}
Output:

1 2 3 4
Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	seq1 := []int{1, 2, 3}
	seq2 := []int{4, 5, 6}

	concatenated := iters.Concat(
		slices.Values(seq1),
		slices.Values(seq2),
	)

	result := slices.Collect(concatenated)

	fmt.Println(result)
}
Output:

[1 2 3 4 5 6]

func Concat2

func Concat2[K, V any](seqs2 ...iter.Seq2[K, V]) iter.Seq2[K, V]

Concat2 is the keyed companion to Concat; it streams every pair from seqs2 in order.

func Contains

func Contains[V comparable](seq iter.Seq[V], value V) bool

Contains reports whether value appears in seq.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	numbers := []int{1, 2, 3, 4, 5}

	hasThree := iters.Contains(
		slices.Values(numbers),
		3,
	)

	hasSix := iters.Contains(
		slices.Values(numbers),
		6,
	)

	fmt.Println(hasThree)
	fmt.Println(hasSix)
}
Output:

true
false

func Contains2

func Contains2[K comparable, V comparable](seq2 iter.Seq2[K, V], key K, value V) bool

Contains2 reports whether the pair (key, value) appears in seq2.

Example (KeyValuePairs)
package main

import (
	"fmt"
	"maps"

	"github.com/picatz/iters"
)

func main() {
	dictionary := map[string]string{
		"apple":  "A fruit",
		"banana": "Another fruit",
		"carrot": "A vegetable",
	}

	hasAppleDefinition := iters.Contains2(
		maps.All(dictionary),
		"apple",
		"A fruit",
	)

	hasBananaWrongDefinition := iters.Contains2(
		maps.All(dictionary),
		"banana",
		"A yellow fruit",
	)

	fmt.Println(hasAppleDefinition)
	fmt.Println(hasBananaWrongDefinition)
}
Output:

true
false

func ContainsFunc

func ContainsFunc[V any](seq iter.Seq[V], fn Predicate[V]) bool

ContainsFunc reports whether any element in seq satisfies fn.

Example (Strings)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	fruit := []string{"apple", "banana", "cherry", "date"}

	hasLongString := iters.ContainsFunc(
		slices.Values(fruit),
		func(s string) bool {
			return len(s) > 5
		},
	)

	hasBString := iters.ContainsFunc(
		slices.Values(fruit),
		func(s string) bool {
			return s[0] == 'b'
		},
	)

	fmt.Println(hasLongString)
	fmt.Println(hasBString)
}
Output:

true
true

func ContainsFunc2

func ContainsFunc2[K, V any](seq2 iter.Seq2[K, V], fn Predicate2[K, V]) bool

ContainsFunc2 reports whether any key/value pair in seq2 satisfies fn.

Example (KeyValuePairs)
package main

import (
	"fmt"
	"maps"

	"github.com/picatz/iters"
)

func main() {
	dictionary := map[string]string{
		"apple":  "A fruit",
		"banana": "Another fruit",
		"carrot": "A vegetable",
	}

	hasDefinitionWithA := iters.ContainsFunc2(
		maps.All(dictionary),
		func(k, v string) bool {
			return v[0] == 'A'
		},
	)

	hasDefinitionWithZ := iters.ContainsFunc2(
		maps.All(dictionary),
		func(k, v string) bool {
			return v[0] == 'Z'
		},
	)

	fmt.Println(hasDefinitionWithA)
	fmt.Println(hasDefinitionWithZ)
}
Output:

true
false

func Context

func Context[T any](ctx context.Context, seq iter.Seq[T]) iter.Seq[T]

Context returns a sequence that yields values from seq until ctx is canceled or seq finishes. Cancellation is checked between elements.

Example (Cancel)
package main

import (
	"context"
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	seq := iters.Context(
		ctx,
		func(yield func(int) bool) {
			for i := 1; i <= 5; i++ {
				if i == 3 {
					cancel()
				}
				if !yield(i) {
					return
				}
			}
		},
	)

	fmt.Println(slices.Collect(seq))
}
Output:

[1 2]

func Context2

func Context2[K, V any](ctx context.Context, seq2 iter.Seq2[K, V]) iter.Seq2[K, V]

Context2 returns a sequence of key/value pairs from seq2 that stops yielding as soon as ctx is canceled.

Example (Cancel)
package main

import (
	"context"
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	seq2 := func(yield func(string, int) bool) {
		pairs := []struct {
			key string
			val int
		}{
			{"a", 1},
			{"b", 2},
			{"c", 3},
		}
		for _, pair := range pairs {
			if pair.key == "b" {
				cancel()
			}
			if !yield(pair.key, pair.val) {
				return
			}
		}
	}

	ctxSeq := iters.Context2(ctx, seq2)

	var keys []string
	for k := range ctxSeq {
		keys = append(keys, k)
	}

	fmt.Println(keys)
}
Output:

[a]

func Equal

func Equal[T comparable](seq1, seq2 iter.Seq[T]) bool

Equal reports whether seq1 and seq2 yield the same elements in the same order. Iteration stops as soon as a mismatch is found.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	a := slices.Values([]int{1, 2, 3})
	b := slices.Values([]int{1, 2, 3})

	fmt.Println(iters.Equal(a, b))
}
Output:

true

func Equal2

func Equal2[K comparable, V comparable](seq1, seq2 iter.Seq2[K, V]) bool

Equal2 reports whether two sequences of key/value pairs yield identical pairs in the same order.

Example (Pairs)
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	seq := func(yield func(string, int) bool) {
		pairs := []struct {
			key string
			val int
		}{
			{"a", 1},
			{"b", 2},
		}
		for _, pair := range pairs {
			if !yield(pair.key, pair.val) {
				return
			}
		}
	}

	fmt.Println(iters.Equal2(seq, seq))
}
Output:

true

func EqualFunc

func EqualFunc[T any](seq1, seq2 iter.Seq[T], equal func(T, T) bool) bool

EqualFunc reports whether seq1 and seq2 yield elements that are equal according to equal. The sequences must produce the same number of items.

Example (Numbers)
package main

import (
	"fmt"
	"math"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	a := slices.Values([]float64{1, 2, math.NaN()})
	b := slices.Values([]float64{1, 2, math.NaN()})

	fmt.Println(
		iters.EqualFunc(
			a,
			b,
			func(x, y float64) bool {
				if math.IsNaN(x) && math.IsNaN(y) {
					return true
				}
				return x == y
			},
		),
	)
}
Output:

true

func EqualFunc2

func EqualFunc2[K any, V any](seq1, seq2 iter.Seq2[K, V], equal func(K, V, K, V) bool) bool

EqualFunc2 reports whether seq1 and seq2 produce key/value pairs that are equal according to equal. The sequences must yield the same number of pairs.

Example (Pairs)
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	seq := func(yield func(string, int) bool) {
		pairs := []struct {
			key string
			val int
		}{
			{"a", 1},
			{"b", 2},
		}
		for _, pair := range pairs {
			if !yield(pair.key, pair.val) {
				return
			}
		}
	}

	fmt.Println(
		iters.EqualFunc2(
			seq,
			seq,
			func(k1 string, v1 int, k2 string, v2 int) bool {
				return k1 == k2 && v1 == v2
			},
		),
	)
}
Output:

true

func Filter

func Filter[V any](seq iter.Seq[V], fn Predicate[V]) iter.Seq[V]

Filter returns a sequence that yields only the elements of seq that make fn return true.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	// Example usage of the Filter function to filter a slice of integers.
	numbers := []int{1, 2, 3, 4, 5}

	// Define the filter function to keep only odd numbers.
	oddNumbers := iters.Filter(
		// Convert the slice to an iter.Seq[int]
		slices.Values(numbers),
		func(num int) bool {
			return num%2 != 0 // Keep only odd numbers
		},
	)

	// Collect the filtered results into a slice.
	result := slices.Collect(oddNumbers)

	fmt.Println(result)
}
Output:

[1 3 5]
Example (Structs)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	// Example usage of the Filter function to filter a slice of structs,
	// which are animals with a name and number of legs in this case.
	type Animal struct {
		Name string
		Legs int
	}

	animals := []Animal{
		{"cat", 4},
		{"dog", 4},
		{"fish", 0},
		{"bird", 2},
	}

	// Define the filter function to keep only animals with more than 2 legs.
	filteredAnimals := iters.Filter(
		// Convert the slice to an iter.Seq[Animal]
		slices.Values(animals),
		func(animal Animal) bool {
			return animal.Legs > 2 // Keep animals with more than 2 legs
		},
	)

	// Collect the filtered results into a slice.
	result := slices.Collect(filteredAnimals)

	fmt.Println(result)
}
Output:

[{cat 4} {dog 4}]

func Filter2

func Filter2[K, V any](seq2 iter.Seq2[K, V], fn Predicate2[K, V]) iter.Seq2[K, V]

Filter2 is the keyed companion to Filter; it forwards only the pairs from seq2 that satisfy fn.

Example (KeyValuePairs)
package main

import (
	"fmt"
	"maps"

	"github.com/picatz/iters"
)

func main() {
	fruits := map[string]string{
		"apple":  "fruit",
		"carrot": "vegetable",
		"pear":   "fruit",
	}

	filtered := iters.Filter2(
		maps.All(fruits),
		func(key, value string) bool {
			return value == "fruit" && key != "pear"
		},
	)

	fmt.Println(maps.Collect(filtered))
}
Output:

map[apple:fruit]

func First

func First[T any](seq iter.Seq[T]) (first T, ok bool)

First returns the first element produced by seq. If seq yields nothing, ok is false and the zero value is returned.

Example (Value)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	fmt.Println(iters.First(slices.Values([]int{4, 5, 6})))
}
Output:

4 true

func First2

func First2[K, V any](seq2 iter.Seq2[K, V]) (firstK K, firstV V, ok bool)

First2 returns the first key/value pair from seq2. If seq2 yields no pairs, ok is false.

Example (Pair)
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	seq2 := func(yield func(string, int) bool) {
		if !yield("a", 1) {
			return
		}
	}
	k, v, ok := iters.First2(seq2)
	fmt.Println(k, v, ok)
}
Output:

a 1 true

func FirstFunc

func FirstFunc[T any](seq iter.Seq[T], pred Predicate[T]) (first T, ok bool)

FirstFunc returns the first element of seq that satisfies pred. If no element matches, ok is false.

Example (Even)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	firstEven, ok := iters.FirstFunc(
		slices.Values([]int{1, 3, 4, 5}),
		func(v int) bool { return v%2 == 0 },
	)
	fmt.Println(firstEven, ok)
}
Output:

4 true

func FirstFunc2

func FirstFunc2[K, V any](seq2 iter.Seq2[K, V], pred Predicate2[K, V]) (firstK K, firstV V, ok bool)

FirstFunc2 returns the first pair from seq2 that satisfies pred. If no pair matches, ok is false.

Example (Match)
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	seq2 := func(yield func(string, int) bool) {
		pairs := []struct {
			key string
			val int
		}{
			{"a", 1},
			{"b", 3},
			{"c", 4},
		}
		for _, pair := range pairs {
			if !yield(pair.key, pair.val) {
				return
			}
		}
	}
	k, v, ok := iters.FirstFunc2(seq2, func(_ string, val int) bool { return val%2 == 0 })
	fmt.Println(k, v, ok)
}
Output:

c 4 true

func Last

func Last[T any](seq iter.Seq[T]) (last T, ok bool)

Last returns the final element produced by seq. If seq yields nothing, ok is false.

Example (Value)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	fmt.Println(iters.Last(slices.Values([]int{1, 2, 3})))
}
Output:

3 true

func Last2

func Last2[K, V any](seq2 iter.Seq2[K, V]) (lastK K, lastV V, ok bool)

Last2 returns the final key/value pair produced by seq2. If seq2 yields nothing, ok is false.

Example (Pair)
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	seq2 := func(yield func(string, int) bool) {
		data := []struct {
			key string
			val int
		}{
			{"a", 1},
			{"b", 2},
		}
		for _, pair := range data {
			if !yield(pair.key, pair.val) {
				return
			}
		}
	}
	k, v, ok := iters.Last2(seq2)
	fmt.Println(k, v, ok)
}
Output:

b 2 true

func LastFunc

func LastFunc[T any](seq iter.Seq[T], pred Predicate[T]) (last T, ok bool)

LastFunc returns the final element of seq that satisfies pred. If no element matches, ok is false.

Example (Gt)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	last, ok := iters.LastFunc(
		slices.Values([]int{1, 4, 2, 5}),
		func(v int) bool { return v > 3 },
	)
	fmt.Println(last, ok)
}
Output:

5 true

func LastFunc2

func LastFunc2[K, V any](seq2 iter.Seq2[K, V], pred Predicate2[K, V]) (lastK K, lastV V, ok bool)

LastFunc2 returns the final key/value pair from seq2 that satisfies pred. If no pair matches, ok is false.

Example (Match)
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	seq2 := func(yield func(string, int) bool) {
		data := []struct {
			key string
			val int
		}{
			{"a", 1},
			{"b", 2},
			{"c", 3},
		}
		for _, pair := range data {
			if !yield(pair.key, pair.val) {
				return
			}
		}
	}
	k, v, ok := iters.LastFunc2(seq2, func(_ string, v int) bool { return v >= 2 })
	fmt.Println(k, v, ok)
}
Output:

c 3 true

func Limit

func Limit[T any](seq iter.Seq[T], n int) iter.Seq[T]

Limit returns a sequence that yields at most n values from seq. When n is zero or negative the returned sequence is empty.

Example (KeyValuePairs)
package main

import (
	"fmt"
	"maps"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	dictionary := map[string]string{
		"apple":  "A fruit",
		"banana": "Another fruit",
		"carrot": "A vegetable",
		"date":   "A sweet fruit",
		"egg":    "A protein source",
	}

	keys := slices.Collect(maps.Keys(dictionary))
	slices.Sort(keys)
	limited := iters.Limit2(
		func(yield func(string, string) bool) {
			for _, key := range keys {
				if !yield(key, dictionary[key]) {
					return
				}
			}
		},
		2,
	)

	resultKeys := []string{}
	resultValues := []string{}
	for key, value := range limited {
		resultKeys = append(resultKeys, key)
		resultValues = append(resultValues, value)
	}

	fmt.Printf("%q\n", resultKeys)
	fmt.Printf("%q\n", resultValues)
}
Output:

["apple" "banana"]
["A fruit" "Another fruit"]
Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	numbers := []int{1, 2, 3, 4, 5}

	limited := iters.Limit(
		slices.Values(numbers),
		3,
	)

	result := slices.Collect(limited)

	fmt.Println(result)
}
Output:

[1 2 3]

func Limit2

func Limit2[K, V any](seq2 iter.Seq2[K, V], n int) iter.Seq2[K, V]

Limit2 returns a sequence that yields at most n key/value pairs from seq2.

func Map

func Map[T, R any](seq iter.Seq[T], fn Mapper[T, R]) iter.Seq[R]

Map returns a sequence whose elements are fn(item) for each element of seq, in order.

func Map2

func Map2[K1, V1, K2, V2 any](seq2 iter.Seq2[K1, V1], fn Mapper2[K1, V1, K2, V2]) iter.Seq2[K2, V2]

Map2 applies fn to every key/value pair from seq2 and yields the resulting pairs.

func Max

func Max[T cmp.Ordered](seq iter.Seq[T]) (max T, ok bool)

Max returns the largest element produced by seq according to Go's ordering for cmp.Ordered types. If seq is empty, ok is false.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	max, ok := iters.Max(slices.Values([]int{3, 1, 4}))
	fmt.Println(max, ok)
}
Output:

4 true

func MaxFunc

func MaxFunc[T any](seq iter.Seq[T], less func(a, b T) bool) (max T, ok bool)

MaxFunc returns the element in seq that maximizes the provided less function (a returns true when a < b). If seq is empty, ok is false.

Example (Custom)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	type Item struct {
		Name  string
		Score int
	}
	items := []Item{{"a", 1}, {"b", 3}}
	max, ok := iters.MaxFunc(
		slices.Values(items),
		func(a, b Item) bool { return a.Score < b.Score },
	)
	fmt.Println(max, ok)
}
Output:

{b 3} true

func Min

func Min[T cmp.Ordered](seq iter.Seq[T]) (min T, ok bool)

Min returns the smallest element produced by seq according to Go's ordering for cmp.Ordered types. If seq is empty, ok is false.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	min, ok := iters.Min(slices.Values([]int{3, 1, 4}))
	fmt.Println(min, ok)
}
Output:

1 true

func MinFunc

func MinFunc[T any](seq iter.Seq[T], less func(a, b T) bool) (min T, ok bool)

MinFunc returns the element in seq that minimizes the provided less function. If seq is empty, ok is false.

Example (Custom)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	type Item struct {
		Name  string
		Score int
	}
	items := []Item{{"a", 1}, {"b", 3}}
	min, ok := iters.MinFunc(
		slices.Values(items),
		func(a, b Item) bool { return a.Score < b.Score },
	)
	fmt.Println(min, ok)
}
Output:

{a 1} true

func Reduce

func Reduce[T, R any](seq iter.Seq[T], fn func(R, T) R, initial R) R

Reduce folds seq into a single value by repeatedly applying fn to the accumulator and the next element. The initial value is returned unchanged if seq is empty.

Example (Sum)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	// Example usage of the Reduce function to sum a slice of integers.
	numbers := []int{1, 2, 3, 4, 5}

	// Define the reduce function to sum the numbers.
	sum := iters.Reduce(
		// Convert the slice to an iter.Seq[int]
		slices.Values(numbers),
		func(acc int, num int) int {
			return acc + num // Sum the numbers
		},
		// Initial value for the sum, which is 0 in this case.
		0,
	)

	fmt.Println(sum)
}
Output:

15

func Reduce2

func Reduce2[K, V, R any](seq2 iter.Seq2[K, V], fn func(R, K, V) R, initial R) R

Reduce2 folds seq2 into a single value by repeatedly calling fn with the accumulator and the next key/value pair.

Example (Collect)
package main

import (
	"fmt"
	"maps"

	"github.com/picatz/iters"
)

func main() {
	input := map[string]int{
		"a": 1,
		"b": 2,
	}

	sum := iters.Reduce2(
		maps.All(input),
		func(acc int, key string, value int) int {
			return acc + value
		},
		0,
	)

	fmt.Println(sum)
}
Output:

3

func Repeat

func Repeat[T any](value T) iter.Seq[T]

Repeat returns an infinite sequence that keeps yielding value until the consumer stops iteration.

Example (Value)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	repeated := iters.Limit(iters.Repeat(7), 3)
	fmt.Println(slices.Collect(repeated))
}
Output:

[7 7 7]

func RepeatFunc

func RepeatFunc[T any](fn func() T) iter.Seq[T]

RepeatFunc returns an infinite sequence that yields the result of fn on every iteration.

Example (Counter)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	counter := 0
	repeated := iters.Limit(
		iters.RepeatFunc(func() int {
			counter++
			return counter
		}),
		3,
	)
	fmt.Println(slices.Collect(repeated))
}
Output:

[1 2 3]

func RepeatN

func RepeatN[T any](value T, count int) iter.Seq[T]

RepeatN yields value count times. When count <= 0 nothing is produced.

Example (Value)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	fmt.Println(slices.Collect(iters.RepeatN("go", 2)))
}
Output:

[go go]

func Reusable

func Reusable[T any](seq iter.Seq[T]) iter.Seq[T]

Reusable caches every element pulled from seq so that the returned sequence can be iterated multiple times. The cache grows until seq is exhausted, so using Reusable with an unbounded input can consume unbounded memory.

Example (Twice)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	reusable := iters.Reusable(slices.Values([]int{1, 2, 3}))

	first := slices.Collect(reusable)
	second := slices.Collect(reusable)

	fmt.Println(first, second)
}
Output:

[1 2 3] [1 2 3]

func Reusable2

func Reusable2[K, V any](seq2 iter.Seq2[K, V]) iter.Seq2[K, V]

Reusable2 caches each key/value pair pulled from seq2 so that the result can be iterated multiple times. It shares the same memory trade-offs as Reusable.

Example (Pairs)
package main

import (
	"fmt"
	"maps"

	"github.com/picatz/iters"
)

func main() {
	source := map[string]int{"a": 1, "b": 2}
	reusable := iters.Reusable2(maps.All(source))

	first := maps.Collect(reusable)
	second := maps.Collect(reusable)

	fmt.Println(first["a"], second["b"])
}
Output:

1 2

func Sort

func Sort[T cmp.Ordered](seq iter.Seq[T]) iter.Seq[T]

Sort collects seq into a slice, sorts it in ascending order using the natural ordering for cmp.Ordered types, and returns a sequence that yields the sorted values.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	sorted := slices.Collect(iters.Sort(slices.Values([]int{3, 1, 2})))
	fmt.Println(sorted)
}
Output:

[1 2 3]

func SortFunc

func SortFunc[T any](seq iter.Seq[T], cmp func(a T, b T) int) iter.Seq[T]

SortFunc behaves like Sort but orders the elements with cmp, matching slices.SortFunc's contract.

Example (Reverse)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	sorted := slices.Collect(
		iters.SortFunc(
			slices.Values([]int{1, 2, 3}),
			func(a, b int) int { return b - a },
		),
	)
	fmt.Println(sorted)
}
Output:

[3 2 1]

func Split

func Split[K, V any](ctx context.Context, seq2 iter.Seq2[K, V]) (iter.Seq[K], iter.Seq[V])

Split duplicates seq2 into two coordinated sequences, one exposing the keys and the other the values. Reads stay synchronized so that each key is paired with its corresponding value even when the consumers progress independently. Iteration stops when ctx is canceled or seq2 finishes.

Example (Map)
package main

import (
	"context"
	"fmt"
	"maps"

	"github.com/picatz/iters"
)

func main() {
	input := map[int]string{
		1: "one",
		2: "two",
		3: "three",
	}

	output := make(map[int]string)

	maps.Insert(
		output,
		iters.Zip(
			iters.Split(
				context.Background(),
				maps.All(input),
			),
		),
	)
	fmt.Println(maps.Equal(output, input))
}
Output:

true

func Stop

func Stop[T comparable](seq iter.Seq[T], stop func(T) bool) iter.Seq[T]

Stop returns a sequence that yields values from seq until stop reports true for an element. The matching element is discarded. If stop never returns true the entire input is forwarded.

Example (Iteration)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	// Example usage of the StopIteration function to stop iteration early.
	numbers := []int{1, 2, 3, 4, 5}

	// Define a function that stops iteration when it encounters the number 3.
	stoppedIteration := iters.Stop(
		// Convert the slice to an iter.Seq[int]
		slices.Values(numbers),
		func(num int) bool {
			return num == 3 // Stop iteration when the number is 3
		},
	)

	// Collect the results into a slice.
	result := slices.Collect(stoppedIteration)

	fmt.Println(result)
}
Output:

[1 2]

func Stop2

func Stop2[K, V comparable](seq iter.Seq2[K, V], stop func(K, V) bool) iter.Seq2[K, V]

Stop2 returns a sequence that yields pairs from seq2 until stop reports true for a key/value pair, excluding the matching pair.

Example (Pairs)
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	type pair struct {
		key string
		val int
	}
	input := []pair{
		{"a", 1},
		{"stop", 0},
		{"b", 2},
	}

	seq2 := func(yield func(string, int) bool) {
		for _, p := range input {
			if !yield(p.key, p.val) {
				return
			}
		}
	}

	stopped := iters.Stop2(seq2, func(k string, _ int) bool { return k == "stop" })

	for k, v := range stopped {
		fmt.Println(k, v)
	}
}
Output:

a 1

func Unique

func Unique[T comparable](seq iter.Seq[T]) iter.Seq[T]

Unique returns a sequence that yields each distinct element from seq once, preserving the first occurrence order. It keeps a set of seen values, so T must be comparable.

Example (Numbers)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	fmt.Println(slices.Collect(iters.Unique(slices.Values([]int{1, 2, 2, 3}))))
}
Output:

[1 2 3]

func UniqueFunc

func UniqueFunc[T any](seq iter.Seq[T], equal func(a, b T) bool) iter.Seq[T]

UniqueFunc behaves like Unique but determines equality with equal, allowing use with non-comparable types.

Example (CaseInsensitive)
package main

import (
	"fmt"
	"slices"
	"strings"

	"github.com/picatz/iters"
)

func main() {
	words := []string{"Go", "go", "iters"}
	unique := iters.UniqueFunc(
		slices.Values(words),
		func(a, b string) bool { return strings.EqualFold(a, b) },
	)
	fmt.Println(slices.Collect(unique))
}
Output:

[Go iters]

func UntilErr

func UntilErr[T any](seq iter.Seq2[T, error]) iter.Seq[T]

UntilErr converts seq into a plain sequence that yields values until a non-nil error appears. The error-causing element is discarded.

Example
package main

import (
	"errors"
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	seq := func(yield func(int, error) bool) {
		if !yield(1, nil) {
			return
		}
		if !yield(0, errors.New("boom")) {
			return
		}
		if !yield(2, nil) {
			return
		}
	}

	fmt.Println(slices.Collect(iters.UntilErr(seq)))
}
Output:

[1]

func WalkErr

func WalkErr[T any](seq iter.Seq2[T, error], fn func(T) bool) error

WalkErr iterates over seq, invoking fn for each value until either fn returns false or seq yields a non-nil error. The first error encountered is returned.

Example
package main

import (
	"fmt"

	"github.com/picatz/iters"
)

func main() {
	seq := func(yield func(string, error) bool) {
		for _, word := range []string{"hello", "iter"} {
			if !yield(word, nil) {
				return
			}
		}
	}

	_ = iters.WalkErr(seq, func(s string) bool {
		fmt.Println(s)
		return true
	})
}
Output:

hello
iter

func Zip

func Zip[T1, T2 any](seq1 iter.Seq[T1], seq2 iter.Seq[T2]) iter.Seq2[T1, T2]

Zip returns a sequence of pairs formed by reading seq1 and seq2 in lockstep. Iteration stops when either input sequence ends.

Example (Sequences)
package main

import (
	"fmt"
	"slices"

	"github.com/picatz/iters"
)

func main() {
	seq := iters.Zip(
		slices.Values([]int{1, 2}),
		slices.Values([]string{"a", "b"}),
	)
	for n, s := range seq {
		fmt.Println(n, s)
	}
}
Output:

1 a
2 b

Types

type Mapper

type Mapper[T, R any] = func(T) R

Mapper describes the transformation applied by Map.

type Mapper2

type Mapper2[K1, V1, K2, V2 any] = func(K1, V1) (K2, V2)

Mapper2 describes the transformation applied by Map2.

type Number

type Number interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
		~float32 | ~float64
}

Number matches any built-in integer or floating-point type.

type Predicate

type Predicate[T any] = func(T) bool

Predicate describes a boolean test applied to elements of a sequence.

type Predicate2

type Predicate2[K, V any] = func(K, V) bool

Predicate2 is the keyed counterpart to Predicate.

type Reducer

type Reducer[R, T any] = func(R, T) R

Reducer describes the accumulator function accepted by Reduce.

type Reducer2

type Reducer2[R, K, V any] = func(R, K, V) R

Reducer2 is the keyed counterpart to Reducer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL