enumerators

package module
v0.1.0-alpha.39 Latest Latest
Warning

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

Go to latest
Published: Jul 25, 2025 License: MIT Imports: 6 Imported by: 26

README

Dependabot Updates ci

enumerators

A small utility library for defining and consuming enumerators in Go. It provides a consistent interface to iterate over slices, channels, and custom generators.

Installation

go get github.com/fgrzl/enumerators

Overview

This library defines a generic Enumerator[T] interface with concrete implementations for common data sources:

  • Slices
  • Channels
  • Callback-based generators

Interface

All enumerators implement the Enumerator[T] interface and extend the Disposable interface:

type Enumerator[T any] interface {
  Disposable
  MoveNext() bool
  Current() (T, error)
  Err() error
}

type Disposable interface {
  Dispose()
}

Important: All enumerators must be disposed when no longer needed to ensure proper resource cleanup. Most functions in this library automatically dispose enumerators for you, but when manually iterating, always call Dispose() or use defer to ensure cleanup.

Built-in Enumerators

SliceEnumerator

Iterates over a Go slice.

e := enumerators.Slice([]int{1, 2, 3})
defer e.Dispose() // Ensure cleanup
for e.MoveNext() {
  v, _ := e.Current()
  fmt.Println(v)
}

### ChannelEnumerator

Iterates over a channel until it's closed.

```go
ch := make(chan string)
go func() {
  ch <- "hello"
  ch <- "world"
  close(ch)
}()

e := enumerators.Channel(context.Background(), 0)
defer e.Dispose() // Ensure cleanup
for e.MoveNext() {
  v, _ := e.Current()
  fmt.Println(v)
}

Automatic Disposal

Many functions in this library automatically dispose enumerators for you:

// ToSlice automatically disposes the enumerator
slice, err := enumerators.ToSlice(enumerators.Slice([]int{1, 2, 3}))

// ForEach automatically disposes the enumerator
err := enumerators.ForEach(enumerators.Slice([]string{"a", "b"}), func(s string) error {
  fmt.Println(s)
  return nil
})

// Consume automatically disposes the enumerator
err := enumerators.Consume(enumerators.Slice([]int{1, 2, 3}))

Chaining Operations

Enumerators can be chained together for complex data processing:

result, err := enumerators.ToSlice(
  enumerators.Map(
    enumerators.Filter(
      enumerators.Slice([]int{1, 2, 3, 4, 5}),
      func(x int) bool { return x%2 == 0 }, // Keep even numbers
    ),
    func(x int) (string, error) { return fmt.Sprintf("num_%d", x), nil }, // Convert to string
  ),
)
// result: []string{"num_2", "num_4"}

Documentation

Overview

Package enumerators provides generic iterator utilities such as chunking, mapping, and cleanup behavior for stream-like processing.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Cleanup

func Cleanup[T any](enumerator Enumerator[T], cleanup func()) *cleanupEnumerator[T]

Cleanup wraps an enumerator with a cleanup function that runs on Dispose.

func Collect

func Collect[T any](enumerator Enumerator[Enumerator[T]]) ([][]T, error)

Collect gathers all chunks into a slice of slices

func Consume

func Consume[T any](e Enumerator[T]) error

Consume advances the enumerator to completion and disposes it. It returns any error encountered during iteration.

func ForEach

func ForEach[T any](enumerator Enumerator[T], do func(T) error) error

ForEach iterates over all elements in the enumerator, calling the provided action function for each element. The enumerator is automatically disposed after iteration completes or when an error occurs. Returns the first error encountered during iteration or from the action function.

func Sum

func Sum[T any, TSum constraints.Ordered](enumerator Enumerator[T], selector func(item T) (TSum, error)) (TSum, error)

Sum consumes an enumerator and returns the sum of all elements after applying a selector function. The enumerator is automatically disposed after computation. Returns an error if enumeration fails or if the selector function returns an error.

func ToMap

func ToMap[T any, TKey comparable, TValue any](enumerator Enumerator[T], keyFn func(T) TKey, valFn func(T) TValue) (map[TKey]TValue, error)

ToMap iterates over the provided enumerator and builds a map by applying the keyFn and valFn to each item. The resulting map uses the key from keyFn(item) and the value from valFn(item). If the enumerator yields an error during iteration, the function returns immediately with that error.

The enumerator is disposed automatically at the end of processing.

Returns an error if iteration fails. If the enumerator is nil, returns nil, nil.

func ToMapWithKey

func ToMapWithKey[TKey comparable, TValue any](enumerator Enumerator[TValue], keyFn func(TValue) TKey) (map[TKey]TValue, error)

ToMapWithKey iterates over the provided enumerator and builds a map using each item as the value. The key for each entry is computed by applying keyFn(item).

The enumerator is disposed automatically at the end of processing.

Returns an error if iteration fails. If the enumerator is nil, returns nil, nil.

func ToSlice

func ToSlice[T any](enumerator Enumerator[T]) ([]T, error)

ToSlice converts an enumerator to a slice by consuming all its elements. The enumerator is automatically disposed after consumption. If the enumerator is already a SliceEnumerator, returns the underlying slice directly.

Types

type ChannelEnumerator

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

ChannelEnumerator provides enumeration over channels with context support. It supports publishing values, error handling, and graceful termination.

func Channel

func Channel[T any](ctx context.Context, size int) *ChannelEnumerator[T]

Channel creates a new channel-based enumerator with the specified buffer size. The enumerator respects the provided context for cancellation.

func (*ChannelEnumerator[T]) Complete

func (e *ChannelEnumerator[T]) Complete()

Complete signals that no more values will be published.

func (*ChannelEnumerator[T]) Current

func (e *ChannelEnumerator[T]) Current() (T, error)

Current returns the current value and any error encountered.

func (*ChannelEnumerator[T]) Dispose

func (e *ChannelEnumerator[T]) Dispose()

Dispose cleans up resources and signals termination.

func (*ChannelEnumerator[T]) Err

func (e *ChannelEnumerator[T]) Err() error

Err returns any error encountered during enumeration.

func (*ChannelEnumerator[T]) Error

func (e *ChannelEnumerator[T]) Error(err error)

Error signals an error to the enumerator.

func (*ChannelEnumerator[T]) MoveNext

func (e *ChannelEnumerator[T]) MoveNext() bool

MoveNext advances the enumerator to the next value in the range. Returns true if more values are available, false otherwise.

func (*ChannelEnumerator[T]) Publish

func (e *ChannelEnumerator[T]) Publish(msg T) bool

Publish sends a value to the enumerator for consumption.

type Disposable

type Disposable interface {
	// Dispose releases any resources held by this object.
	// It should be safe to call Dispose multiple times.
	Dispose()
}

Disposable represents an object that holds resources that need to be explicitly released. Implementers should ensure that Dispose can be called multiple times safely.

type Enumerator

type Enumerator[T any] interface {
	Disposable
	MoveNext() bool
	Current() (T, error)
	Err() error
}

Enumerator represents a generic iterator over a sequence of values of type T. It should be disposed when no longer needed.

func Chain

func Chain[T any](enumerators ...Enumerator[T]) Enumerator[T]

Chain creates an enumerator that sequentially chains multiple enumerators together. Elements from each enumerator are yielded in order until that enumerator is exhausted, then the next enumerator in the chain begins yielding elements.

func Chunk

func Chunk[T any, TSize constraints.Ordered](
	in Enumerator[T],
	target TSize,
	compute func(item T) (TSize, error),
) Enumerator[Enumerator[T]]

func ChunkByCount

func ChunkByCount[T any](in Enumerator[T], count int) Enumerator[Enumerator[T]]

func ChunkByKey

func ChunkByKey[V any, K comparable](
	in Enumerator[V],
	keyFunc func(V) K,
) Enumerator[KeyedChunk[K, V]]

ChunkByKey splits an input stream into chunks based on a computed key. Each chunk yields items sharing the same key, wrapped in a KeyedChunk.

func ChunkWhen

func ChunkWhen[V any, K comparable](
	in Enumerator[V],
	split func(item V) (K, bool, error),
) Enumerator[KeyedChunk[K, V]]

ChunkWhen groups items into chunks based on a split function. It emits KeyedChunk[K, V] where each chunk has a key and a stream of values.

func Empty

func Empty[T any]() Enumerator[T]

Empty returns an enumerator that yields no elements.

func Error

func Error[T any](err error) Enumerator[T]

Error creates an enumerator that immediately returns the specified error. This is useful for creating error states in enumerator chains.

func Filter

func Filter[T any](parent Enumerator[T], filter func(T) bool) Enumerator[T]

Filter creates an enumerator that only yields elements satisfying the predicate function. The filter function receives an element of type T and returns true if the element should be included.

func FilterMap

func FilterMap[TIn any, TOut any](enumerator Enumerator[TIn], apply func(TIn) (TOut, bool, error)) Enumerator[TOut]

FilterMap creates an enumerator that applies both transformation and filtering in a single pass. The apply function receives an element and returns (transformedValue, shouldInclude, error). Only elements where shouldInclude is true are yielded after transformation.

func FlatMap

func FlatMap[T any, U any](parent Enumerator[T], mapper func(T) Enumerator[U]) Enumerator[U]

FlatMap creates an enumerator that applies a function to each element and flattens the results. The mapper function receives an element of type T and returns an enumerator of type U. All elements from each returned enumerator are yielded in sequence.

func Generate

func Generate[T any](next func() (T, bool, error)) Enumerator[T]

Generate creates a new enumerator using a generator function. The next function should return (value, hasNext, error) where hasNext indicates if more values are available.

func GenerateAndDispose

func GenerateAndDispose[T any](next func() (T, bool, error), dispose func()) Enumerator[T]

GenerateAndDispose creates a new enumerator with both generator and disposal functions. The dispose function is called when the enumerator is disposed to clean up resources.

func GenerateFromMap

func GenerateFromMap[K comparable, V any](m map[K]V) Enumerator[*KeyValuePair[K, V]]

GenerateFromMap creates an enumerator that yields key-value pairs from a map. The order of enumeration is not guaranteed to be consistent across calls.

func Group

func Group[T any, G comparable](
	in Enumerator[T],
	compute func(item T) (G, error),
) Enumerator[*Grouping[T, G]]

func Interleave

func Interleave[T any, TOrdered constraints.Ordered](
	enumerators []Enumerator[T],
	key func(T) TOrdered,
) Enumerator[T]

Interleave creates a new interleave enumerator that merges multiple enumerators based on the ordering provided by the key function.

func Map

func Map[T any, U any](enumerator Enumerator[T], mapper func(T) (U, error)) Enumerator[U]

Map creates an enumerator that applies a transformation function to each element. The mapper function receives an element of type T and returns a transformed element of type U. If the mapper function returns an error, enumeration stops and the error is propagated.

func PageItemEnumerator

func PageItemEnumerator[T any](fetchPage func() ([]T, bool, error)) Enumerator[T]

func Range

func Range[T any](seed int, count int, factory func(i int) T) Enumerator[T]

Range creates an enumerator that generates a sequence of values using a factory function. It starts at 'seed' and generates 'count' values by calling factory(i) for i from seed to seed+count-1.

func SkipIf

func SkipIf[T any](enumerator Enumerator[T], condition func(T) bool) Enumerator[T]

SkipIf creates an enumerator that skips elements satisfying the specified condition. Elements are skipped when condition(element) returns true.

func Slice

func Slice[T any](slice []T) Enumerator[T]

Slice creates a new enumerator that iterates over the provided slice.

func Take

func Take[T any](enumerator Enumerator[T], take int) Enumerator[T]

Take creates an enumerator that yields at most the specified number of elements. If take is negative or zero, the enumerator will yield no elements.

func TakeWhile

func TakeWhile[T any](enumerator Enumerator[T], condition func(T) bool) Enumerator[T]

TakeWhile creates an enumerator that yields elements while the specified condition is satisfied. Enumeration stops as soon as any element fails the condition.

type Generator

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

Generator generates values continuously.

func (*Generator[T]) Current

func (ce *Generator[T]) Current() (T, error)

Current returns the current value or an error if disposed.

func (*Generator[T]) Dispose

func (ce *Generator[T]) Dispose()

Dispose cleans up the enumerator.

func (*Generator[T]) Err

func (ce *Generator[T]) Err() error

Err returns the last error.

func (*Generator[T]) MoveNext

func (ce *Generator[T]) MoveNext() bool

MoveNext generates the next value.

type GroupEnumerator

type GroupEnumerator[T any, G comparable] struct {
	// contains filtered or unexported fields
}

func (*GroupEnumerator[T, G]) Current

func (e *GroupEnumerator[T, G]) Current() (*Grouping[T, G], error)

func (*GroupEnumerator[T, G]) Dispose

func (e *GroupEnumerator[T, G]) Dispose()

func (*GroupEnumerator[T, G]) Err

func (e *GroupEnumerator[T, G]) Err() error

func (*GroupEnumerator[T, G]) MoveNext

func (e *GroupEnumerator[T, G]) MoveNext() bool

type Grouping

type Grouping[T any, G comparable] struct {
	Enumerator *innerGroupEnumerator[T, G]
	Key        G
}

type GroupingSlice

type GroupingSlice[T any, G comparable] struct {
	Items []T
	Group G
}

func CollectGroupingSlices

func CollectGroupingSlices[T any, G comparable](enumerator Enumerator[*Grouping[T, G]]) (groupSlices []*GroupingSlice[T, G], err error)

CollectGroupingSlices gathers all chunks into a slice of slices

type KeyValuePair

type KeyValuePair[K comparable, V any] struct {
	Key   K
	Value V
}

KeyValuePair represents a key-value pair for map enumeration.

type KeyedChunk

type KeyedChunk[K comparable, V any] struct {
	Key   K
	Chunk Enumerator[V]
}

KeyedChunk represents a chunk of items that share the same key.

type PeekableEnumerator

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

PeekableEnumerator wraps an Enumerator and adds peek functionality

func Peekable

func Peekable[T any](inner Enumerator[T]) *PeekableEnumerator[T]

Peekable constructs a PeekableEnumerator

func (*PeekableEnumerator[T]) Current

func (p *PeekableEnumerator[T]) Current() (T, error)

Current returns the current element

func (*PeekableEnumerator[T]) Dispose

func (p *PeekableEnumerator[T]) Dispose()

Dispose releases resources

func (*PeekableEnumerator[T]) Err

func (p *PeekableEnumerator[T]) Err() error

Err returns the last error from the underlying enumerator

func (*PeekableEnumerator[T]) HasNext

func (p *PeekableEnumerator[T]) HasNext() bool

HasNext checks if there is a next element available without advancing the enumerator

func (*PeekableEnumerator[T]) MoveNext

func (p *PeekableEnumerator[T]) MoveNext() bool

MoveNext advances to the next element

func (*PeekableEnumerator[T]) Peek

func (p *PeekableEnumerator[T]) Peek() (T, bool, error)

Peek allows looking at the next element without advancing the enumerator

type SliceEnumerator

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

SliceEnumerator provides enumeration over a Go slice.

func (*SliceEnumerator[T]) Current

func (e *SliceEnumerator[T]) Current() (T, error)

Current returns the current element and any error encountered.

func (*SliceEnumerator[T]) Dispose

func (enumerator *SliceEnumerator[T]) Dispose()

Dispose cleans up resources. For SliceEnumerator, this is a no-op.

func (*SliceEnumerator[T]) Err

func (e *SliceEnumerator[T]) Err() error

Err returns any error encountered during enumeration.

func (*SliceEnumerator[T]) MoveNext

func (e *SliceEnumerator[T]) MoveNext() bool

MoveNext advances the enumerator to the next element in the slice. Returns true if more elements are available, false otherwise.

Jump to

Keyboard shortcuts

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