Skip to content

renaldid/iterx

Repository files navigation

iterx

Go Reference Go Version License: MIT Coverage

Composable, lazy iterator adapters for Go 1.23+.

iterx fills the gap left by the stdlib: Go 1.23 introduced iter.Seq[V] as the standard iterator type, but provides no way to compose them — you cannot write .Filter().Map().Take() and there is no official adapter library (the x/exp/xiter proposal has been open since 2023). iterx is that library.

result := iterx.From(slices.Values(products)).
    Filter(func(p Product) bool { return p.InStock }).
    Take(10).
    Collect()

Zero dependencies · 100% test coverage · Works with stdlib for range


Installation

go get github.com/renaldid/iterx

Requires Go 1.23 or later.


Quick examples

// Filter and collect
evens := iterx.Collect(iterx.Filter(iterx.Range(1, 11), func(n int) bool {
    return n%2 == 0
}))
// [2 4 6 8 10]

// Map changes the element type
names := iterx.Collect(iterx.Map(slices.Values(users), func(u User) string {
    return u.Name
}))

// Fluent chaining via Chain
top5 := iterx.From(slices.Values(scores)).
    Filter(func(s int) bool { return s >= 60 }).
    Drop(0).
    Take(5).
    Collect()

// Works with standard for range
for i, v := range iterx.Enumerate(slices.Values(items)) {
    fmt.Printf("[%d] %v\n", i, v)
}

// Sum / Min / Max
total := iterx.Sum(iterx.Map(slices.Values(orders), func(o Order) float64 {
    return o.Amount
}))

// Chunk into pages
for page := range iterx.Chunk(slices.Values(rows), 100) {
    insertBatch(page)
}

API

Transforms

Function Signature Description
Filter (Seq[V], func(V) bool) → Seq[V] Keep elements where f is true
Map (Seq[In], func(In) Out) → Seq[Out] Transform each element
FlatMap (Seq[In], func(In) Seq[Out]) → Seq[Out] Map then flatten
Flatten (Seq[Seq[V]]) → Seq[V] Merge nested iterators
Peek (Seq[V], func(V)) → Seq[V] Side-effect per element; passes through unchanged
Take (Seq[V], n) → Seq[V] First n elements
Drop (Seq[V], n) → Seq[V] Skip first n elements
TakeWhile (Seq[V], func(V) bool) → Seq[V] Take while predicate is true
DropWhile (Seq[V], func(V) bool) → Seq[V] Drop while predicate is true
StepBy (Seq[V], n) → Seq[V] Every n-th element (panics if n < 1)
Chunk (Seq[V], n) → Seq[[]V] Non-overlapping slices of n elements
Window (Seq[V], n) → Seq[[]V] Sliding windows of n elements
Concat (Seq[V]...) → Seq[V] Concatenate multiple iterators
Zip (Seq[A], Seq[B]) → Seq2[A, B] Pair elements; stops at shorter
Enumerate (Seq[V]) → Seq2[int, V] Pair with zero-based index
Unique (Seq[V]) → Seq[V] Remove duplicates (first occurrence kept)
Sorted (Seq[V]) → Seq[V] Collect, sort ascending, re-iterate
SortedBy (Seq[V], cmp) → Seq[V] Collect, sort with custom comparator

Sources

Function Description
Of(vals...) Iterator over literal values
Empty[V]() Iterator that yields nothing
Range(start, stop) Integers in [start, stop)
RangeStep(start, stop, step) Integers with custom step (panics if step == 0)
Repeat(v) Infinite repetition of v — use with Take
RepeatN(v, n) Repeat v exactly n times
Generate(seed, next) Infinite sequence — use with Take

Terminal operations

Function Description
Collect(seq) → []V
First(seq) → (V, bool) — first element
Last(seq) → (V, bool) — last element
Nth(seq, n) → (V, bool) — n-th element (0-based)
Count(seq) → int — number of elements
Any(seq, f) → bool — true if any element satisfies f (short-circuits)
All(seq, f) → bool — true if all elements satisfy f (short-circuits)
None(seq, f) → bool — true if no element satisfies f
ForEach(seq, f) Call f on every element
Reduce(seq, init, f) Left fold; accumulator type may differ from V
GroupBy(seq, key) → map[K][]V
Partition(seq, f) → (yes []V, no []V)

Numeric helpers

Function Constraint Description
Sum[V] Number Sum of all elements
Min[V] cmp.Ordered Smallest element
Max[V] cmp.Ordered Largest element
MinBy[V] any Smallest by custom comparator
MaxBy[V] any Largest by custom comparator

Number covers all built-in integer and float types (int, int8int64, uintuintptr, float32, float64) including custom types built on them.

Seq2 helpers

Functions that operate on iter.Seq2[K, V] (two-value iterators such as those produced by maps.All, Zip, or Enumerate):

Function Description
Keys(seq2) Iterator over keys
Values(seq2) Iterator over values
CollectMap(seq2) → map[K]V
Filter2(seq2, f) Keep pairs where f(k, v) is true
Map2(seq2, f) Transform each key-value pair
Swap(seq2) Exchange key and value positions

Chain — fluent chaining

From(seq) wraps any iter.Seq[V] in a Chain[V], making same-type adapters available as methods:

iterx.From(slices.Values(nums)).
    Filter(isPositive).
    Drop(5).
    Take(10).
    Collect()

Methods on Chain[V]: Filter, Take, Drop, TakeWhile, DropWhile, Peek, StepBy, Concat, Collect, First, Last, Count, Any, All, None, ForEach, Reduce, Seq.

Seq() unwraps the chain back to a plain iter.Seq[V], useful when handing off to a free function:

// Chain → free function → Chain again
iterx.From(
    iterx.Map(
        iterx.From(slices.Values(users)).Filter(isActive).Seq(),
        func(u User) string { return u.Name },
    ),
).Take(10).Collect()

Chunk and Window are not available as Chain methods because they change the element type to []V, which causes an instantiation cycle in Go's generics system. Use them as free functions:

iterx.From(iterx.Chunk(myChain.Seq(), 3))

Design notes

Lazy by default. Every adapter returns a new iter.Seq without reading the source. Work only happens when you range over or collect the result.

Zero allocation on the hot path. Filter, Map, Take, Drop, and similar adapters create only a closure; no slices are allocated until you call Collect. Chunk, Window, Sorted, and Unique allocate internally because they must buffer elements.

Fail fast on invalid arguments. Chunk, Window, StepBy, and RangeStep panic immediately (before returning the iterator) on invalid input (n < 1 or step == 0). This surfaces programmer errors at the call site rather than silently during iteration.

Compatible with stdlib for range. Every iter.Seq[V] returned by this package works directly in Go 1.23 range loops:

for v := range iterx.Filter(slices.Values(items), pred) {
    // ...
}

No global state. All functions are pure. Multiple goroutines may safely call any function concurrently as long as they hold separate iterators — each call creates independent closure state.


License

MIT

About

Composable, lazy iterator adapters for Go 1.23+ iter.Seq — Filter, Map, FlatMap, Chunk, Zip, Sorted, and more. Zero dependencies.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages