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], size int) iter.Seq[[]T]
- func BatchWithWaitLimit[T any](i iter.Seq[T], size int, waitLimit time.Duration) 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 func(K, V) KV) []KV
- func CollectErr[T any](i iter.Seq[T], e ErrFunc) ([]T, error)
- func CollectErrIter[T any](i iter.Seq2[T, error]) ([]T, error)
- 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](i iter.Seq[T], filter func(T) bool) iter.Seq[T]
- func First[T any](i iter.Seq[T]) (T, bool)
- func First2[K, V any](i iter.Seq2[K, V]) (K, V, bool)
- func ForEach[T any](i iter.Seq[T], fn func(T) error, errFuncs ...ErrFunc) (rErr error)
- 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 IntRange(begin, end int) iter.Seq[int]
- func Last[T any](i iter.Seq[T]) (T, 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 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 Once[T any](i iter.Seq[T]) iter.Seq[T]
- func Once2[K, V any](i iter.Seq2[K, V]) iter.Seq2[K, V]
- func Reduce[R, T any](i iter.Seq[T], initial R, fn func(R, T) R) R
- func ReduceErr[R, T any](i iter.Seq[T], 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 TakeAll[T any](next func() (T, bool)) []T
- func ToChan[T any](itr iter.Seq[T]) (_ <-chan T, cancel func())
- func WithConcurrentAccess[T any](i iter.Seq[T]) (iter.Seq[T], func())
- type ErrFunc
- func BufioScanner[T string | []byte](s *bufio.Scanner, closer io.Closer, errFuncs ...ErrFunc) (iter.Seq[T], ErrFunc)
- func FromErrIter[T any](i ErrIter[T]) (iter.Seq[T], ErrFunc)
- func MapErr[To any, From any](i iter.Seq[From], transform func(From) (To, error), errs ...ErrFunc) (iter.Seq[To], ErrFunc)
- func Paginate[T any](ctx context.Context, ...) (iter.Seq[T], ErrFunc)
- type ErrIter
- func Error[T any](err error) ErrIter[T]
- func ErrorF[T any](format string, a ...any) ErrIter[T]
- func FromPullIter[T any](itr PullIter[T]) ErrIter[T]
- func OnErrIterValue[To any, From any](itr ErrIter[From], pipeline func(itr iter.Seq[From]) iter.Seq[To]) ErrIter[To]
- func ToErrIter[T any](i iter.Seq[T], errFuncs ...ErrFunc) ErrIter[T]
- type KV
- type PullIter
Examples ¶
Constants ¶
const Break errorkit.Error = `iterators:break`
Variables ¶
This section is empty.
Functions ¶
func BatchWithWaitLimit ¶
Example ¶
var slow iter.Seq[int] = func(yield func(int) bool) { for { if 5 < rnd.IntBetween(0, 10) { // random slowness time.Sleep(time.Second / 3) } if !yield(rnd.Int()) { return } } } batched := iterkit.BatchWithWaitLimit(slow, 7, time.Second) for vs := range batched { fmt.Printf("%#v\n", vs) }
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 }
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)) }
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
func CollectErrIter ¶
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.CollectErrIter(itr) _, _ = vs, err
func CollectPull ¶
Example ¶
var itr iter.Seq[int] = iterkit.IntRange(1, 10) vs := iterkit.CollectPull(iter.Pull(itr)) _ = vs
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
func Count2 ¶
Example ¶
itr := maps.All(map[string]int{ "foo": 2, "bar": 4, "baz": 8, }) iterkit.Count2(itr) // 3
func Empty ¶
Empty iterator is used to represent nil result with Null object pattern
Example ¶
_ = iterkit.Empty[any]()
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) }
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
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
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) }
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
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) })
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 })
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 { }
func SingleValue ¶
SingleValue creates an iterator that can return one single element and will ensure that Next can only be called once.
Types ¶
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 BufioScanner ¶
func FromErrIter ¶
FromErrIter will split an iter.Seq2[T, error] iterator into a iter.Seq[T] iterator plus an error retrival func.
Example ¶
var sourceErrIter iter.Seq2[int, error] i, errFunc := iterkit.FromErrIter(sourceErrIter) for v := range i { fmt.Println(v) } if err := errFunc(); err != nil { fmt.Println(err.Error()) }
func MapErr ¶
func MapErr[To any, From any](i iter.Seq[From], transform func(From) (To, error), errs ...ErrFunc) (iter.Seq[To], ErrFunc)
Example ¶
rawNumbers := iterkit.Slice([]string{"1", "2", "42"}) numbers, finish := iterkit.MapErr[int](rawNumbers, strconv.Atoi) _ = finish _ = numbers
func Paginate ¶
func Paginate[T any]( ctx context.Context, more func(ctx context.Context, offset int) (values []T, hasNext bool, _ error), errFuncs ...ErrFunc, ) (iter.Seq[T], ErrFunc)
Paginate 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(ctx context.Context, offset int) ([]Foo, bool, 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, false, err } var values []FooDTO defer resp.Body.Close() dec := json.NewDecoder(resp.Body) dec.DisallowUnknownFields() if err := dec.Decode(&values); err != nil { return nil, false, err } vs, err := dtokit.Map[[]Foo](ctx, values) if err != nil { return nil, false, err } probablyHasNextPage := len(vs) == limit return vs, probablyHasNextPage, nil } foos, release := iterkit.Paginate(ctx, 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. _ = release
type ErrIter ¶
ErrIter 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 FromPullIter ¶
func OnErrIterValue ¶
func OnErrIterValue[To any, From any](itr ErrIter[From], pipeline func(itr iter.Seq[From]) iter.Seq[To]) ErrIter[To]
OnErrIterValue will apply a iterator pipeline on a given ErrIter
Example ¶
var ( input iter.Seq2[int, error] output iter.Seq2[string, error] ) output = iterkit.OnErrIterValue(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
func ToErrIter ¶
ToErrIter 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.ToErrIter(seq1Iter) for v, err := range errIter { if err != nil { // will be always nil for the []int slice } _ = v // 1, 2, 3... }
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