iter

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: MIT Imports: 5 Imported by: 0

README

iter

Lazy, composable, type-safe pipelines for Go 1.27 iterators — the chill sibling of samber/lo, built on range-over-func.

iter.Of(1, 2, 3, 4, 5, 6).
    Filter(func(v int) bool { return v%2 == 0 }).
    Map(func(v int) int { return v * v }).
    Tap(func(v int) { log.Println("even square:", v) }).
    Collect()
// [4 16 36]

Nothing runs until the terminal method — and you are the driver. break, Take, done. Upstream stops working the instant you stop asking. No pull iterators, no Next(), no ceremony.

Why you'll like it

  • Actually reads well. Go 1.27's generic methods turn of(x).filter(f). map(g).collect() into a fluent chain instead of paren soup.
  • Lazy or get out. Every op is a range-over-func. Work happens only when — and for as long as — you ask for elements.
  • Errors are just data. MapErr / FlatMapErr emit (result, error) pairs. Log them, drop them, or collect them. The stream never stops.
  • Zero dependencies. iter, slices, maps, cmp, errors. That's it. That's the whole party.

Install

go get github.com/lrweck/iter

Needs Go 1.27.

Pipeline methods

Method What it does
Map(f) Apply f to every element
Filter(f) Keep elements where f is true
SkipErr(check) Keep elements where check returns nil, skip the rest
UniqByFunc(key) Keep the first element of each equal key (any T)
FlatMap(f) Flatten the sequences produced by f
Take(n) / Drop(n) Keep / drop the first n elements
TakeWhile(p) / DropWhile(p) Keep / drop elements while p is true
Tap(f) Peek at every element, pass it through (Elixir's tap/2)
Enumerate() Attach an index → Seq2[int, T]
Zip(o) Pair up corresponding elements of two sequences

When the element type needs a constraint a method can't apply, you get a package function — same vibe, shorter stack:

iter.Uniq(iter.Of(1, 2, 2, 1))                        // []int{1, 2}
iter.Of(users).UniqByFunc(func(u user) int { return u.id })

The tail of the pipeline:

vals := iter.Of(3, 1, 4, 1).Tap(func(v int) { log.Printf("%d", v) }).Collect() // []int
first, ok := iter.Of(9, 8).First()                          // 9, true
last, ok := iter.Of(9, 8).Last()                            // 8, true
total := iter.Sum(iter.Of(1, 2, 3))                         // 6
best, ok := iter.Max(iter.Of(3, 1, 4))                      // 4, true
pop, ok := iter.Of(users).MaxByFunc(func(u user) int { return u.age })
pairs := iter.ToMap(iter.Of("a", "b").Enumerate())          // map[int]string
buckets := iter.Of(vals).GroupByFunc(func(v int) string { return ... })
pick := iter.Of(vals).KeyByFunc(func(v int) string { return ... })

Generators:

iter.Range(2, 5).Collect()                                                 // []int{2, 3, 4}
iter.Repeat(3, "x").Collect()                                              // []string{"x", "x", "x"}
iter.RepeatBy(3, func(i int) int { return i * i }).Collect()               // []int{0, 1, 4}
iter.From(slices.Values([]string{"a", "b"}))                               // wrap any stdlib iterator
iter.Concat(iter.Of(1), iter.Of(2, 3)).Collect()                         // []int{1, 2, 3}
iter.Contains(iter.Of(3, 1, 4), 4)                                       // true
iter.ToMap(iter.FromMap(m))                                              // map round-trip
iter.FromKeys(m).Collect(), iter.FromValues(m).Collect()                 // the map's keys / values
for v := range iter.Of(1, 2, 3).Seq() { ... }                            // or range directly

Predicate terminals bail at the first decisive element:

iter.Of(1, 3, 4).Some(isEven)      // true, 4 never even gets looked at
iter.Of(2, 4).Every(isEven)        // true
iter.Of(1, 3).None(isEven)         // true
iter.Of(1, 3, 4).Find(isEven)      // 4, true
iter.Of(codes).CountBy(isEven)     // int

Bad input? Whatever, keep going

Fallible ops never kill the stream. Every element pairs with an outcome as a Result[V], and you own what failures mean:

parsed := iter.Of("1", "junk", "2").MapErr(strconv.Atoi)

// log failures as they flow through
parsed.Tap(func(v int, err error) {
    if err != nil {
        log.Printf("bad line: %v", err)
    }
}).CollectErr()

// filter the failures out, keep the wins
good := parsed.IgnoreErr().Collect() // []int{1, 2}

// ... or grab values and every failure at once
values, err := parsed.CollectErr() // []int{1, 2}, errors.Join of all failures
if err != nil {
    log.Printf("%v", err)
}

Errors behave the Go way: errors.Is to compare, %w to wrap.

Why it's shaped like that

Go won't let a generic method constrain its own receiver's type. Three things fall off the method set because of it:

  • Sum / Max / Min / Uniq need numeric, ordered, or comparable element types, so they're package functions. Their keyed counterparts — SumByFunc / MaxByFunc / MinByFunc / UniqByFunc / KeyByFunc / GroupByFunc — shove the constraint onto the key and work as methods for any T.
  • Chunk returns Seq[[]T], and the compiler chases T → []T → ... through the method set until it gives up.
  • Error recovery pins the pair slot to error, which generic Seq2 methods can't say — so there's a dedicated Result[V] type whose methods (Take, Filter, IgnoreErr, Errors, CollectErr, Tap) speak error fluently.

License

MIT

Documentation

Overview

Package iter provides lazy, composable sequences over Go 1.27 iterators.

Values flow through a pipeline of generic methods, so lookup cost is paid only on the final terminal method:

iter.Of(1, 2, 3, 4).Filter(isEven).Map(square).Collect()

Range-over-func makes consumers the driver: breaking out of a `range` on a returned sequence stops upstream work immediately.

Fallible operations (MapErr, FlatMapErr) run every element and pair the outcome as a Result[U], a sequence of (result, error): (result, nil) on success, (zero, err) on failure. The error travels as data, so it can be logged with Tap, dropped with IgnoreErr, collected with Errors, or surfaced with CollectErr.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Contains

func Contains[T comparable](s Seq[T], v T) bool

Contains reports whether the sequence contains v. Like Uniq, it is a package function because comparable applies to the element type; predicate checks are covered by Some.

func Max

func Max[T cmp.Ordered](s Seq[T]) (T, bool)

Max returns the greatest element, or ok=false if the sequence is empty.

func Min

func Min[T cmp.Ordered](s Seq[T]) (T, bool)

Min returns the least element, or ok=false if the sequence is empty.

func Sum

func Sum[T Number](s Seq[T]) T

Sum adds all elements. The empty sequence sums to zero.

func ToMap

func ToMap[K comparable, V any](s Seq2[K, V]) map[K]V

ToMap eagerly collects a pair sequence into a map.

Types

type Number

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

Number is a constraint for all builtin numeric types, including complex (which cannot be ordered, so Max/Min stay on cmp.Ordered). It cannot be a method receiver constraint, so summing lives here as a package function, as math/rand/v2 does with its intType.

type Result

type Result[V any] struct {
	// contains filtered or unexported fields
}

Result is a fallible sequence: pairs of (result, error), where success is (result, nil) and failure is (zero, err). The errors travel as data, so they can be inspected or dropped without stopping the pipeline.

It is a Seq2 with the second type argument pre-bound to error, which is what lets its methods express error semantics that generic Seq2 methods cannot (Go does not allow a method to constrain its receiver's type parameters).

func FromResult

func FromResult[V any](seq stditer.Seq2[V, error]) Result[V]

FromResult wraps a standard pair iterator as a fallible sequence.

func (Result[V]) CollectErr

func (s Result[V]) CollectErr() ([]V, error)

CollectErr eagerly evaluates the fallible sequence, returning every successful value and all failures joined into one error with errors.Join, or nil if nothing failed.

func (Result[V]) Count

func (s Result[V]) Count() int

Count returns the number of outcome pairs.

func (Result[V]) Errors

func (s Result[V]) Errors() Seq[error]

Errors yields the error of each failed pair, for logging or counting.

func (Result[V]) Filter

func (s Result[V]) Filter(f func(V, error) bool) Result[V]

Filter keeps the outcome pairs for which f returns true.

func (Result[V]) IgnoreErr

func (s Result[V]) IgnoreErr() Seq[V]

IgnoreErr drops the error pairs and yields only the successful values as a plain sequence from here on: the errors are skipped and the value is kept.

func (Result[V]) Seq

func (s Result[V]) Seq() stditer.Seq2[V, error]

Seq exposes the underlying standard pair iterator, for ranging directly:

for v, err := range res.Seq() { ... }

func (Result[V]) Take

func (s Result[V]) Take(n int) Result[V]

Take keeps at most n outcome pairs, stopping the upstream pull that early.

func (Result[V]) Tap

func (s Result[V]) Tap(f func(V, error)) Result[V]

Tap inspects every outcome as it flows through, then yields it unchanged. This is where fallible pipelines log their errors.

type Seq

type Seq[T any] struct {
	// contains filtered or unexported fields
}

Seq is a lazy sequence of values of type T.

func Chunk

func Chunk[T any](s Seq[T], n int) Seq[[]T]

Chunk groups the sequence into slices of at most n elements. It panics if n is not positive.

Chunk is a package function, not a method: a generic method returning Seq[[]T] instantiates the receiver's own type with []T and trips the compiler's instantiation-cycle check.

func Concat

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

Concat concatenates the sequences in order.

func From

func From[T any](seq stditer.Seq[T]) Seq[T]

From wraps a standard iterator into a Seq.

func FromKeys

func FromKeys[K comparable, V any](m map[K]V) Seq[K]

FromKeys yields the keys of a map.

func FromValues

func FromValues[K comparable, V any](m map[K]V) Seq[V]

FromValues yields the values of a map.

func Of

func Of[T any](vals ...T) Seq[T]

Of builds a Seq from its arguments.

func Range

func Range(from, to int) Seq[int]

Range yields from up to, but not including, to, stepping by 1.

func Repeat

func Repeat[T any](n int, v T) Seq[T]

Repeat yields n copies of v.

func RepeatBy

func RepeatBy[T any](n int, f func(int) T) Seq[T]

RepeatBy yields the values produced by f(0), f(1), ..., f(n-1).

func Uniq

func Uniq[T comparable](s Seq[T]) Seq[T]

Uniq keeps only the first occurrence of each equal element, deduplicating the sequence. Like Max/Min/Sum, it is a package function because its comparable constraint applies to the element type, which a method cannot re-restrict; the key-based variant UniqByFunc handles non-comparable elements as a method.

func (Seq[T]) Collect

func (s Seq[T]) Collect() []T

Collect eagerly evaluates the sequence into a slice.

func (Seq[T]) Count

func (s Seq[T]) Count() int

Count returns the number of elements.

func (Seq[T]) CountBy

func (s Seq[T]) CountBy(pred func(T) bool) int

CountBy counts the elements for which pred is true.

func (Seq[T]) Drop

func (s Seq[T]) Drop(n int) Seq[T]

Drop discards the first n elements.

func (Seq[T]) DropWhile

func (s Seq[T]) DropWhile(pred func(T) bool) Seq[T]

DropWhile discards elements from the start while pred is true.

func (Seq[T]) Each

func (s Seq[T]) Each(f func(T))

Each drives the sequence, calling f for every element.

func (Seq[T]) Enumerate

func (s Seq[T]) Enumerate() Seq2[int, T]

Enumerate pairs each element with its index.

func (Seq[T]) Every

func (s Seq[T]) Every(pred func(T) bool) bool

Every reports whether pred is true for every element, stopping early.

func (Seq[T]) Filter

func (s Seq[T]) Filter(f func(T) bool) Seq[T]

Filter keeps elements for which f returns true.

func (Seq[T]) Find

func (s Seq[T]) Find(pred func(T) bool) (T, bool)

Find returns the first element for which pred is true, or ok=false.

func (Seq[T]) First

func (s Seq[T]) First() (T, bool)

First returns the first element, or ok=false if the sequence is empty.

func (Seq[T]) FlatMap

func (s Seq[T]) FlatMap[U any](f func(T) Seq[U]) Seq[U]

FlatMap concatenates the sequences produced by f.

func (Seq[T]) FlatMapErr

func (s Seq[T]) FlatMapErr[U any](f func(T) (Seq[U], error)) Result[U]

FlatMapErr is FlatMap with an error source: each element produces either a run of (result, nil) pairs or a single (zero, err) pair.

func (Seq[T]) GroupByFunc

func (s Seq[T]) GroupByFunc[K comparable](f func(T) K) map[K][]T

GroupByFunc buckets elements by the key produced by f, returning a map of the buckets.

func (Seq[T]) KeyByFunc

func (s Seq[T]) KeyByFunc[K comparable](key func(T) K) map[K]T

KeyByFunc pivots a single element per key: the last element for each key wins.

func (Seq[T]) Last

func (s Seq[T]) Last() (T, bool)

Last returns the final element, or ok=false if the sequence is empty.

func (Seq[T]) Map

func (s Seq[T]) Map[U any](f func(T) U) Seq[U]

Map applies f to each element.

func (Seq[T]) MapErr

func (s Seq[T]) MapErr[U any](f func(T) (U, error)) Result[U]

MapErr applies f to each element, pairing every outcome: (result, nil) on success, (zero, err) on failure. The error pairs never stop the pipeline; handling them is up to the consumer (IgnoreErr, Errors, CollectErr).

func (Seq[T]) MaxByFunc

func (s Seq[T]) MaxByFunc[K cmp.Ordered](key func(T) K) (T, bool)

MaxByFunc returns the element with the greatest key, or ok=false if the sequence is empty.

func (Seq[T]) MinByFunc

func (s Seq[T]) MinByFunc[K cmp.Ordered](key func(T) K) (T, bool)

MinByFunc returns the element with the least key, or ok=false if the sequence is empty.

func (Seq[T]) None

func (s Seq[T]) None(pred func(T) bool) bool

None reports whether pred is false for every element.

func (Seq[T]) Reduce

func (s Seq[T]) Reduce[U any](init U, f func(U, T) U) U

Reduce folds the sequence into a single value starting from init.

func (Seq[T]) ReduceErr

func (s Seq[T]) ReduceErr[U any](init U, f func(U, T) (U, error)) (U, error)

ReduceErr is Reduce with a fallible step. A failing step is skipped (the accumulator is kept) and remembered as the first error; evaluation runs to the end, so every value is processed.

func (Seq[T]) Seq

func (s Seq[T]) Seq() stditer.Seq[T]

Seq exposes the underlying standard iterator, for ranging directly:

for v := range seq.Seq() { ... }

func (Seq[T]) SkipErr

func (s Seq[T]) SkipErr(check func(T) error) Seq[T]

SkipErr keeps elements for which check returns nil, skipping the failures and moving on to the next one.

func (Seq[T]) Some

func (s Seq[T]) Some(pred func(T) bool) bool

Some reports whether pred is true for at least one element, stopping early.

func (Seq[T]) SumByFunc

func (s Seq[T]) SumByFunc[K Number](key func(T) K) K

SumByFunc sums the keys produced by key. The empty sequence sums to zero.

func (Seq[T]) Take

func (s Seq[T]) Take(n int) Seq[T]

Take keeps at most n elements.

func (Seq[T]) TakeWhile

func (s Seq[T]) TakeWhile(pred func(T) bool) Seq[T]

TakeWhile yields elements from the start while pred is true.

func (Seq[T]) Tap

func (s Seq[T]) Tap(f func(T)) Seq[T]

Tap inspects every element as it flows through, then yields it unchanged. It is the Go counterpart of Elixir's |> tap/2: a hook for logging and side effects in the middle of a pipeline.

func (Seq[T]) UniqByFunc

func (s Seq[T]) UniqByFunc[K comparable](key func(T) K) Seq[T]

UniqByFunc keeps only the first element of each run of equal keys, deduplicating by the comparable key produced by key.

func (Seq[T]) Zip

func (s Seq[T]) Zip[U any](o Seq[U]) Seq2[T, U]

Zip pairs each element with the corresponding element of o, stopping at the shorter sequence.

type Seq2

type Seq2[K, V any] struct {
	// contains filtered or unexported fields
}

Seq2 is a lazy sequence of key/value pairs.

func From2

func From2[K, V any](seq stditer.Seq2[K, V]) Seq2[K, V]

From2 wraps a standard pair iterator into a Seq2.

func FromMap

func FromMap[K comparable, V any](m map[K]V) Seq2[K, V]

FromMap wraps map iteration as a key/value Seq2 (order not defined).

func (Seq2[K, V]) Count

func (s Seq2[K, V]) Count() int

Count returns the number of pairs.

func (Seq2[K, V]) Keys

func (s Seq2[K, V]) Keys() Seq[K]

Keys yields the keys of a pair sequence.

func (Seq2[K, V]) Seq

func (s Seq2[K, V]) Seq() stditer.Seq2[K, V]

Seq exposes the underlying standard pair iterator, for ranging directly:

for k, v := range pairs.Seq() { ... }

func (Seq2[K, V]) Tap

func (s Seq2[K, V]) Tap(f func(K, V)) Seq2[K, V]

Tap inspects every pair as it flows through, then yields it unchanged; see Seq.Tap.

func (Seq2[K, V]) Values

func (s Seq2[K, V]) Values() Seq[V]

Values yields the values of a pair sequence.

Jump to

Keyboard shortcuts

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