Documentation
¶
Overview ¶
package iterators provide iterator implementations.
Summary ¶
An Iterator's goal is to decouple the origin of the data from the consumer who uses that data. Most commonly, iterators hide whether the data comes from a specific database, standard input, or elsewhere. This approach helps to design data consumers that are not dependent on the concrete implementation of the data source, while still allowing for the composition and various actions on the received data stream. An Iterator represents an iterable list of element, which length is not known until it is fully iterated, thus can range from zero to infinity. As a rule of thumb, if the consumer is not the final destination of the data stream, it should use the pipeline pattern to avoid bottlenecks with local resources such as memory.
Resources ¶
https://en.wikipedia.org/wiki/Iterator_pattern https://en.wikipedia.org/wiki/Pipeline_(software)
Index ¶
- Constants
- func Batch[T any](i iter.Seq[T], opts ...BatchOption) iter.Seq[[]T]
- func Chan[T any](ch <-chan T) iter.Seq[T]
- func CharRange(begin, end rune) iter.Seq[rune]
- func Collect[T any](i iter.Seq[T]) []T
- func Collect2[K, V, KV any](i iter.Seq2[K, V], m KVMapFunc[KV, K, V]) []KV
- func Collect2Map[K comparable, V any](i iter.Seq2[K, V]) map[K]V
- func CollectErr[T any](i iter.Seq2[T, error]) ([]T, error)
- func CollectErrIter[T any](i iter.Seq2[T, error]) ([]T, error)deprecated
- func CollectPull[T any](next func() (T, bool), stops ...func()) []T
- func CollectPullIter[T any](itr PullIter[T]) ([]T, error)
- func Count[T any](i iter.Seq[T]) int
- func Count2[K, V any](i iter.Seq2[K, V]) int
- func Empty[T any]() iter.Seq[T]
- func Empty2[T1, T2 any]() iter.Seq2[T1, T2]
- func Filter[T any, Iter I1[T]](i Iter, filter func(T) bool) Iter
- func Filter2[K, V any](i iter.Seq2[K, V], filter func(k K, v V) bool) iter.Seq2[K, V]
- func First[T any](i iter.Seq[T]) (T, bool)
- func First2[K, V any](i iter.Seq2[K, V]) (K, V, bool)
- func FromKV[K, V any](kvs []KV[K, V]) iter.Seq2[K, V]
- func FromPull[T any](next func() (T, bool), stops ...func()) iter.Seq[T]
- func FromPull2[K, V any](next func() (K, V, bool), stops ...func()) iter.Seq2[K, V]
- func Head[T any](i iter.Seq[T], n int) iter.Seq[T]
- func Head2[K, V any](i iter.Seq2[K, V], n int) iter.Seq2[K, V]
- func IntRange(begin, end int) iter.Seq[int]
- func Last[T any](i iter.Seq[T]) (T, bool)
- func Last2[K, V any](i iter.Seq2[K, V]) (K, V, bool)
- func Limit[V any](i iter.Seq[V], n int) iter.Seq[V]
- func Map[To any, From any](i iter.Seq[From], transform func(From) To) iter.Seq[To]
- func Map2[OKey, OVal, IKey, IVal any](i iter.Seq2[IKey, IVal], transform func(IKey, IVal) (OKey, OVal)) iter.Seq2[OKey, OVal]
- func Merge[T any](is ...iter.Seq[T]) iter.Seq[T]
- func Merge2[K, V any](is ...iter.Seq2[K, V]) iter.Seq2[K, V]
- func Offset[V any](i iter.Seq[V], offset int) iter.Seq[V]
- func Reduce[R, T any](i iter.Seq[T], initial R, fn func(R, T) R) R
- func ReduceErr[R, T any, I I1[T]](i I, initial R, fn func(R, T) (R, error)) (result R, rErr error)
- func Reverse[T any](i iter.Seq[T]) iter.Seq[T]
- func SingleValue[T any](v T) iter.Seq[T]
- func Slice[T any](slice []T) iter.Seq[T]
- func Take[T any](next func() (T, bool), n int) []T
- func Take2[KV any, K, V any](next func() (K, V, bool), n int, m KVMapFunc[KV, K, V]) []KV
- func Take2All[KV any, K, V any](next func() (K, V, bool), m KVMapFunc[KV, K, V]) []KV
- func TakeAll[T any](next func() (T, bool)) []T
- func ToChan[T any](itr iter.Seq[T]) (_ <-chan T, cancel func())
- type BatchConfig
- type BatchOption
- type ErrFunc
- type ErrIterdeprecated
- type ErrSeq
- func Error[T any](err error) ErrSeq[T]
- func ErrorF[T any](format string, a ...any) ErrSeq[T]
- func FromPages[T any](next func(offset int) (values []T, _ error)) ErrSeq[T]
- func FromPullIter[T any](itr PullIter[T]) ErrSeq[T]
- func MapErr[To any, From any, Iter I1[From]](i Iter, transform func(From) (To, error)) ErrSeq[To]
- func OnErrIterValue[To any, From any](itr ErrSeq[From], pipeline func(itr iter.Seq[From]) iter.Seq[To]) ErrSeq[To]deprecated
- func OnErrSeqValue[To any, From any](itr ErrSeq[From], pipeline func(itr iter.Seq[From]) iter.Seq[To]) ErrSeq[To]
- func ToErrIter[T any](i iter.Seq[T], errFuncs ...ErrFunc) ErrSeq[T]deprecated
- func ToErrSeq[T any](i iter.Seq[T], errFuncs ...ErrFunc) ErrSeq[T]
- type I1
- type KV
- type KVMapFunc
- type PullIter
- type SingleUseErrSeq
- type SingleUseSeq
- type SingleUseSeq2
Examples ¶
- Batch
- Batch (WithSize)
- Batch (WithWaitLimit)
- Chan
- CharRange
- Collect
- Collect2
- Collect2Map
- CollectErr
- CollectKV
- CollectPull
- Count
- Count2
- Empty
- Filter
- Filter (WithErrSeq)
- First2
- FromPages
- Head
- Head2
- IntRange
- Last
- Last2
- Map
- Map2
- MapErr
- OnErrSeqValue
- Reduce
- ReduceErr
- Reverse
- SplitErrSeq
- Sync
- Sync2
- Take2
- TakeAll
- ToErrSeq
Constants ¶
const NoMore errorkit.Error = "[[ErrNoMorePage]]"
Variables ¶
This section is empty.
Functions ¶
func Batch ¶
Example ¶
src := iterkit.IntRange(0, 1000) batched := iterkit.Batch(src) for vs := range batched { fmt.Printf("%#v\n", vs) }
Output:
Example (WithSize) ¶
src := iterkit.IntRange(0, 1000) batched := iterkit.Batch(src, iterkit.BatchSize(100)) for vs := range batched { fmt.Printf("%#v\n", vs) }
Output:
Example (WithWaitLimit) ¶
slowIterSeq := iterkit.IntRange(0, 1000) batched := iterkit.Batch(slowIterSeq, iterkit.BatchWaitLimit(time.Second)) // Batching will occure either when the batching size reached // or when the wait limit duration passed for vs := range batched { fmt.Printf("%#v\n", vs) }
Output:
func Chan ¶
Chan creates an iterator out from a channel
Example ¶
ch := make(chan int) i := iterkit.Chan(ch) go func() { defer close(ch) ch <- 42 }() for v := range i { fmt.Println(v) // 42 once }
Output:
func CharRange ¶
CharRange returns an iterator that will range between the specified `begin“ and the `end` rune.
Example ¶
for char := range iterkit.CharRange('A', 'Z') { // prints characters between A and Z // A, B, C, D... Z fmt.Println(string(char)) }
Output:
func Collect2 ¶
Example ¶
var itr iter.Seq2[string, int] type T struct { S string I int } ints := iterkit.Collect2(itr, func(s string, i int) T { return T{S: s, I: i} }) _ = ints
Output:
func Collect2Map ¶ added in v0.294.0
func Collect2Map[K comparable, V any](i iter.Seq2[K, V]) map[K]V
Collect2Map will collect2 an iter.Seq2 into a map.
Example ¶
var values iter.Seq2[string, int] = func(yield func(string, int) bool) { if !yield("foo", 42) { return } if !yield("bar", 7) { return } if !yield("baz", 13) { return } } vs := iterkit.Collect2Map(values) _ = vs // map[string]int{"foo": 42, "bar": 7, "baz": 13}
Output:
func CollectErr ¶
Example ¶
var itr iter.Seq2[int, error] = func(yield func(int, error) bool) { for i := 0; i < 42; i++ { if !yield(i, nil) { return } } } vs, err := iterkit.CollectErr(itr) _, _ = vs, err
Output:
func CollectPull ¶
Example ¶
var itr iter.Seq[int] = iterkit.IntRange(1, 10) vs := iterkit.CollectPull(iter.Pull(itr)) _ = vs
Output:
func CollectPullIter ¶
func Count ¶
Count will iterate over and count the total iterations number
Good when all you want is count all the elements in an iterator but don't want to do anything else.
Example ¶
i := iterkit.Slice[int]([]int{1, 2, 3}) total := iterkit.Count[int](i) _ = total // 3
Output:
func Count2 ¶
Example ¶
itr := maps.All(map[string]int{ "foo": 2, "bar": 4, "baz": 8, }) iterkit.Count2(itr) // 3
Output:
func Empty ¶
Empty iterator is used to represent nil result with Null object pattern
Example ¶
_ = iterkit.Empty[any]()
Output:
func Filter ¶
Example ¶
var i iter.Seq[int] i = iterkit.Slice([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) i = iterkit.Filter[int](i, func(n int) bool { return n > 2 }) for v := range i { fmt.Println(v) }
Output:
Example (WithErrSeq) ¶
var repo crud.AllFinder[Foo] all, _ := repo.FindAll(context.Background()) hasBar := iterkit.Filter(all, func(foo Foo) bool { return foo.Bar != "" }) _ = hasBar
Output:
func First2 ¶
First2 decode the first next value of the iterator and close the iterator
Example ¶
var itr iter.Seq2[string, int] = func(yield func(string, int) bool) { for i := 0; i < 42; i++ { if !yield(strconv.Itoa(i), i) { return } } } k, v, ok := iterkit.First2(itr) _, _, _ = k, v, ok
Output:
func Head ¶
Head takes the first n element, similarly how the coreutils "head" app works.
Example ¶
inf42 := func(yield func(int) bool) { for /* infinite */ { if !yield(42) { return } } } i := iterkit.Head[int](inf42, 3) vs := iterkit.Collect(i) _ = vs // []{42, 42, 42}, nil
Output:
func Head2 ¶ added in v0.294.0
Head2 takes the first n element, similarly how the coreutils "head" app works.
Example ¶
inf42 := func(yield func(int, int) bool) { for /* infinite */ { if !yield(42, 24) { return } } } i := iterkit.Head2[int](inf42, 3) vs := iterkit.Collect2Map(i) _ = vs // map[int]int{42:24, 42:24, 42:24}, nil
Output:
func IntRange ¶
IntRange returns an iterator that will range between the specified `begin“ and the `end` int.
Example ¶
for n := range iterkit.IntRange(1, 9) { // prints characters between 1 and 9 // 1, 2, 3, 4, 5, 6, 7, 8, 9 fmt.Println(n) }
Output:
func Last ¶
Example ¶
itr := iterkit.IntRange(0, 10) n, ok := iterkit.Last(itr) _ = ok // true _ = n // 10
Output:
func Last2 ¶ added in v0.287.0
Example ¶
var itr iter.Seq2[int, string] = func(yield func(int, string) bool) { for n := range iterkit.IntRange(0, 10) { if !yield(n, strconv.Itoa(n)) { return } } } num, str, ok := iterkit.Last2(itr) _ = ok // true _ = num // 10 _ = str // "10"
Output:
func Map ¶
Map allows you to do additional transformation on the values. This is useful in cases, where you have to alter the input value, or change the type all together. Like when you read lines from an input stream, and then you map the line content to a certain data structure, in order to not expose what steps needed in order to deserialize the input stream, thus protect the business rules from this information.
Example ¶
rawNumbers := iterkit.Slice([]string{"1", "2", "42"}) numbers := iterkit.Map[int](rawNumbers, func(v string) int { return len(v) }) _ = numbers
Output:
func Map2 ¶ added in v0.294.0
func Map2[OKey, OVal, IKey, IVal any](i iter.Seq2[IKey, IVal], transform func(IKey, IVal) (OKey, OVal)) iter.Seq2[OKey, OVal]
Example ¶
itr := maps.All(map[int]string{1: "1", 2: "2", 3: "42"}) numbers := iterkit.Map2[int, int](itr, func(k int, v string) (int, int) { return k, len(v) }) _ = numbers
Output:
func Reduce ¶
Example ¶
raw := iterkit.Slice([]int{1, 2, 42}) _ = iterkit.Reduce[[]int](raw, nil, func(vs []int, v int) []int { return append(vs, v) })
Output:
func ReduceErr ¶
Example ¶
raw := iterkit.Slice([]string{"1", "2", "42"}) _, _ = iterkit.ReduceErr[[]int](raw, nil, func(vs []int, raw string) ([]int, error) { v, err := strconv.Atoi(raw) if err != nil { return nil, err } return append(vs, v), nil })
Output:
func Reverse ¶
Reverse will reverse the iteration direction.
WARNING ¶
It does not work with infinite iterators, as it requires to collect all values before it can reverse the elements.
Example ¶
itr := iterkit.IntRange(1, 3) // []int{1, 2, 3} itr = iterkit.Reverse(itr) // []int{3, 2, 1} for range itr { }
Output:
func SingleValue ¶
SingleValue creates an iterator that can return one single element and will ensure that Next can only be called once.
func Take2 ¶ added in v0.295.0
Take will take the next N value from a pull iterator.
Example ¶
kvs := maps.All(map[string]int{ "foo": 42, "bar": 7, "baz": 13, }) next, stop := iter.Pull2(kvs) defer stop() type E struct { Key string Value int } es := iterkit.Take2[E](next, 3, func(k string, v int) E { return E{Key: k, Value: v} }) _ = len(es) // 3
Output:
Types ¶
type BatchConfig ¶ added in v0.295.0
func (BatchConfig) Configure ¶ added in v0.295.0
func (c BatchConfig) Configure(t *BatchConfig)
type BatchOption ¶ added in v0.295.0
type BatchOption option.Option[BatchConfig]
func BatchSize ¶ added in v0.295.0
func BatchSize(n int) BatchOption
func BatchWaitLimit ¶ added in v0.295.0
func BatchWaitLimit(d time.Duration) BatchOption
type ErrFunc ¶
ErrFunc is the check function that can tell if currently an iterator that is related to the error function has an issue or not.
func SplitErrSeq ¶ added in v0.295.0
SplitErrSeq will split an iter.Seq2[T, error] iterator into a iter.Seq[T] iterator plus an error retrival func.
Example ¶
var sourceErrSeq iter.Seq2[int, error] i, errFunc := iterkit.SplitErrSeq(sourceErrSeq) for v := range i { fmt.Println(v) } if err := errFunc(); err != nil { fmt.Println(err.Error()) }
Output:
type ErrSeq ¶ added in v0.295.0
ErrSeq is an iterator that can tell if a currently returned value has an issue or not.
func Error ¶
Error returns an Interface that only can do is returning an Err and never have next element
func FromPages ¶ added in v0.295.0
FromPages will create an iter.Seq[T] which can be used like any other iterator, Under the hood the "more" function will be used to dynamically retrieve more values when the previously called values are already used up.
If the more function has a hard-coded true for the "has next page" return value, then the pagination will interpret an empty result as "no more pages left".
Example ¶
ctx := context.Background() fetchMoreFoo := func(offset int) ([]Foo, error) { const limit = 10 query := url.Values{} query.Set("limit", strconv.Itoa(limit)) query.Set("offset", strconv.Itoa(offset)) resp, err := http.Get("https://api.mydomain.com/v1/foos?" + query.Encode()) if err != nil { return nil, err } var values []FooDTO defer resp.Body.Close() dec := json.NewDecoder(resp.Body) dec.DisallowUnknownFields() if err := dec.Decode(&values); err != nil { return nil, err } vs, err := dtokit.Map[[]Foo](ctx, values) if err != nil { return nil, err } if len(vs) < limit { return vs, iterkit.NoMore } return vs, nil } foos := iterkit.FromPages(fetchMoreFoo) _ = foos // foos can be called like any iterator, // and under the hood, the fetchMoreFoo function will be used dynamically, // to retrieve more values when the previously called values are already used up.
Output:
func FromPullIter ¶
func MapErr ¶
Example ¶
rawNumbers := iterkit.Slice([]string{"1", "2", "42"}) numbers := iterkit.MapErr[int](rawNumbers, strconv.Atoi) _ = numbers
Output:
func OnErrSeqValue ¶ added in v0.295.0
func OnErrSeqValue[To any, From any](itr ErrSeq[From], pipeline func(itr iter.Seq[From]) iter.Seq[To]) ErrSeq[To]
OnErrSeqValue will apply a iterator pipeline on a given ErrSeq
Example ¶
var ( input iter.Seq2[int, error] output iter.Seq2[string, error] ) output = iterkit.OnErrSeqValue(input, func(itr iter.Seq[int]) iter.Seq[string] { // we receive an iterator without the error second value // we do our iterator manipulation like it doesn't have an error // then we return it back itr = iterkit.Map(itr, func(v int) int { return v * 3 }) itr = iterkit.Filter(itr, func(i int) bool { return i%2 == 0 }) return iterkit.Map(itr, strconv.Itoa) }) // the returned iter have the pipeline applied, // but the elements still contain the potential error value in case something went wrong. _ = output
Output:
func ToErrSeq ¶ added in v0.295.0
ToErrSeq will turn a iter.Seq[T] into an iter.Seq2[T, error] iterator, and use the error function to yield potential issues with the iteration.
Example ¶
seq1Iter := iterkit.Slice([]int{1, 2, 3}) errIter := iterkit.ToErrSeq(seq1Iter) for v, err := range errIter { if err != nil { // will be always nil for the []int slice } _ = v // 1, 2, 3... }
Output:
type PullIter ¶
type PullIter[V any] interface { // Next will ensure that Value returns the next item when executed. // If the next value is not retrievable, Next should return false and ensure Err() will return the error cause. Next() bool // Value returns the current value in the iterator. // The action should be repeatable without side effects. Value() V // Closer is required to make it able to cancel iterators where resources are being used behind the scene // for all other cases where the underling io is handled on a higher level, it should simply return nil io.Closer // Err return the error cause. Err() error }
PullIter define a separate object that encapsulates accessing and traversing an aggregate object. Clients use an iterator to access and traverse an aggregate without knowing its representation (data structures). Interface design inspirited by https://golang.org/pkg/encoding/json/#Decoder https://en.wikipedia.org/wiki/Iterator_pattern
func ToPullIter ¶
type SingleUseErrSeq ¶ added in v0.295.0
SingleUseErrSeq is an iter.Seq2[T, error] that can only iterated once. After iteration, it is expected to yield no more values. For more information on single use sequences, please read the documentation of SingleUseSeq.
func BufioScanner ¶
type SingleUseSeq ¶ added in v0.287.0
SingleUseSeq is an iter.Seq[T] that can only iterated once. After iteration, it is expected to yield no more values.
Most iterators provide the ability to walk an entire sequence: when called, the iterator does any setup necessary to start the sequence, then calls yield on successive elements of the sequence, and then cleans up before returning. Calling the iterator again walks the sequence again.
SingleUseSeq iterators break that convention, providing the ability to walk a sequence only once. These “single-use iterators” typically report values from a data stream that cannot be rewound to start over. Calling the iterator again after stopping early may continue the stream, but calling it again after the sequence is finished will yield no values at all.
If an iterator Sequence is single use, it should either has comments for functions or methods that it return single-use iterators or it should use the SingleUseSeq to clearly express it with a return type.
func Sync ¶ added in v0.286.0
func Sync[T any](i iter.Seq[T]) (SingleUseSeq[T], func())
Sync ensures that an iterator can be safely used by multiple goroutines at the same time.
Example ¶
src := iterkit.IntRange(0, 100) itr, cancel := iterkit.Sync(src) defer cancel() var g tasker.JobGroup[tasker.FireAndForget] for range 2 { g.Go(func(ctx context.Context) error { for v := range itr { _ = v // use v } return nil }) } g.Join()
Output:
type SingleUseSeq2 ¶ added in v0.287.0
SingleUseSeq2 is an iter.Seq2[K, V] that can only iterated once. After iteration, it is expected to yield no more values. For more information on single use sequences, please read the documentation of SingleUseSeq.
func Sync2 ¶ added in v0.286.0
func Sync2[K, V any](i iter.Seq2[K, V]) (SingleUseSeq2[K, V], func())
Sync2 ensures that an iterator can be safely used by multiple goroutines at the same time.
Example ¶
src := iterkit.IntRange(0, 100) itr, cancel := iterkit.Sync2(iterkit.ToErrSeq(src)) defer cancel() var g tasker.JobGroup[tasker.FireAndForget] for range 2 { g.Go(func(ctx context.Context) error { for v, err := range itr { _ = err // handle err _ = v // use v } return nil }) } g.Join()
Output: