goiteratorhelper

package module
v0.0.14 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2024 License: MIT Imports: 0 Imported by: 0

README

go-iterator-helper

GoDoc

Helpers / converters / sources for iterators.

hiter

Helpers for iterator.

This package avoids re-implementing those which defined in standard or quasi-standard libraries. Namely slices, maps, x/exp/xiter.

For example, Zip, Reduce are not defined since they will be implemented in xiter when #61898 accepted and merged.

Some ideas are stolen from https://jsr.io/@std/collections/doc, like Permutation and SumOf.

Iterator sources: functions that compose up iterators from data sources:

func Chan[V any](ctx context.Context, ch <-chan V) iter.Seq[V]
func Heap[V any](h heap.Interface) iter.Seq[V]
func ListAll[V any](l *list.List) iter.Seq[V]
func ListElementAll[V any](ele *list.Element) iter.Seq[V]
func ListBackward[V any](l *list.List) iter.Seq[V]
func ListElementBackward[V any](ele *list.Element) iter.Seq[V]
func RingAll[V any](r *ring.Ring) iter.Seq[V]
func RingBackward[V any](r *ring.Ring) iter.Seq[V]
func Empty[V any]() iter.Seq[V]
func Empty2[K, V any]() iter.Seq2[K, V]
func JsonDecoder(dec *json.Decoder) iter.Seq2[json.Token, error]
func XmlDecoder(dec *xml.Decoder) iter.Seq2[xml.Token, error]
func IndexAccessible[A Atter[T], T any](a A, indices iter.Seq[int]) iter.Seq2[int, T]
func Decode[V any, Dec interface{  Decode(any) error }](dec Dec) iter.Seq2[V, error]
func Values2[S ~[]KeyValue[K, V], K, V any](s S) iter.Seq2[K, V]
func MergeSort[S ~[]T, T cmp.Ordered](m S) iter.Seq[T]
func MergeSortFunc[S ~[]T, T any](m S, cmp func(l, r T) int) iter.Seq[T]
func MergeSortSliceLike[S SliceLike[T], T cmp.Ordered](s S) iter.Seq[T]
func MergeSortSliceLikeFunc[S SliceLike[T], T any](s S, cmp func(l, r T) int) iter.Seq[T]
func Once[V any](v V) iter.Seq[V]
func Once2[K, V any](k K, v V) iter.Seq2[K, V]
func Permutations[S ~[]E, E any](in S) iter.Seq[S]
func Range[T Numeric](start, end T) iter.Seq[T]
func RangeInclusive[T Numeric](start, end T, includeStart, includeEnd bool) iter.Seq[T]
func Repeat[V any](v V, n int) iter.Seq[V]
func Repeat2[K, V any](k K, v V, n int) iter.Seq2[K, V]
func RepeatFunc[V any](fnV func() V, n int) iter.Seq[V]
func RepeatFunc2[K, V any](fnK func() K, fnV func() V, n int) iter.Seq2[K, V]
func Scan(scanner *bufio.Scanner) iter.Seq[string]
func ScanErr(scanner *bufio.Scanner) iter.Seq2[string, error]
func SqlRows[T any](r *sql.Rows, scanner func(*sql.Rows) (T, error)) iter.Seq2[T, error]
func Nexter[T any, Nexter interface { Next() bool; Err() error }](n Nexter, scanner func(Nexter) (T, error)) iter.Seq2[T, error]
func Step[N Numeric](initial, step N) iter.Seq[N]
func StepBy[V any](initial, step int, v []V) iter.Seq2[int, V]
func StringsChunk(s string, n int) iter.Seq[string]
func StringsRuneChunk(s string, n int) iter.Seq[string]
func StringsSplitFunc(s string, n int, splitFn StringsCutterFunc) iter.Seq[string]
func SyncMap[K, V any](m *sync.Map) iter.Seq2[K, V]
func Window[S ~[]E, E any](s S, n int) iter.Seq[S]

Iterator adapters: iterator that processes / modifies values from other iterators.

func Alternate[V any](seqs ...iter.Seq[V]) iter.Seq[V]
func Alternate2[K, V any](seqs ...iter.Seq2[K, V]) iter.Seq2[K, V]
func AssertValue[V any](seq iter.Seq[reflect.Value]) iter.Seq[V]
func AssertValue2[K, V any](seq iter.Seq2[reflect.Value, reflect.Value]) iter.Seq2[K, V]
func Assert[V any](seq iter.Seq[any]) iter.Seq[V]
func Assert2[K, V any](seq iter.Seq2[any, any]) iter.Seq2[K, V]
func CheckEach[V any](n int, check func(v V, i int) bool, seq iter.Seq[V]) iter.Seq[V]
func CheckEach2[K, V any](n int, check func(k K, v V, i int) bool, seq iter.Seq2[K, V]) iter.Seq2[K, V]
func Compact[V comparable](seq iter.Seq[V]) iter.Seq[V]
func CompactFunc[V any](eq func(i, j V) bool, seq iter.Seq[V]) iter.Seq[V]
func Compact2[K, V comparable](seq iter.Seq2[K, V]) iter.Seq2[K, V]
func CompactFunc2[K, V any](eq func(k1 K, v1 V, k2 K, v2 V) bool, seq iter.Seq2[K, V]) iter.Seq2[K, V]
func Decorate[V any](prepend, append Iterable[V], seq iter.Seq[V]) iter.Seq[V]
func Decorate2[K, V any](prepend, append Iterable2[K, V], seq iter.Seq2[K, V]) iter.Seq2[K, V]
func Flatten[S ~[]E, E any](seq iter.Seq[S]) iter.Seq[E]
func FlattenSeq[V any](seq iter.Seq[iter.Seq[V]]) iter.Seq[V]
func FlattenSeq2[K, V any](seq iter.Seq[iter.Seq2[K, V]]) iter.Seq2[K, V]
func FlattenF[S1 ~[]E1, E1 any, E2 any](seq iter.Seq2[S1, E2]) iter.Seq2[E1, E2]
func FlattenL[S2 ~[]E2, E1 any, E2 any](seq iter.Seq2[E1, S2]) iter.Seq2[E1, E2]
func FlattenSeqF[K, V any](seq iter.Seq2[iter.Seq[K], V]) iter.Seq2[K, V]
func FlattenSeqL[K, V any](seq iter.Seq2[K, iter.Seq[V]]) iter.Seq2[K, V]
func ToKeyValue[K, V any](seq iter.Seq2[K, V]) iter.Seq[KeyValue[K, V]]
func FromKeyValue[K, V any](seq iter.Seq[KeyValue[K, V]]) iter.Seq2[K, V]
func LimitUntil[V any](f func(V) bool, seq iter.Seq[V]) iter.Seq[V]
func LimitUntil2[K, V any](f func(K, V) bool, seq iter.Seq2[K, V]) iter.Seq2[K, V]
func LimitAfter[V any](f func(V) bool, seq iter.Seq[V]) iter.Seq[V]
func LimitAfter2[K, V any](f func(K, V) bool, seq iter.Seq2[K, V]) iter.Seq2[K, V]
func RunningReduce[V, Sum any](reducer func(accumulator Sum, current V, i int) Sum, initial Sum, seq iter.Seq[V]) iter.Seq[Sum]
func Skip[V any](n int, seq iter.Seq[V]) iter.Seq[V]
func Skip2[K, V any](n int, seq iter.Seq2[K, V]) iter.Seq2[K, V]
func SkipLast[V any](n int, seq iter.Seq[V]) iter.Seq[V]
func SkipLast2[K, V any](n int, seq iter.Seq2[K, V]) iter.Seq2[K, V]
func SkipWhile[V any](f func(V) bool, seq iter.Seq[V]) iter.Seq[V]
func SkipWhile2[K, V any](f func(K, V) bool, seq iter.Seq2[K, V]) iter.Seq2[K, V]
func Tap[V any](tap func(V), seq iter.Seq[V]) iter.Seq[V]
func Tap2[K, V any](tap func(K, V), seq iter.Seq2[K, V]) iter.Seq2[K, V]
func Enumerate[T any](seq iter.Seq[T]) iter.Seq2[int, T]
func Pairs[K, V any](seq1 iter.Seq[K], seq2 iter.Seq[V]) iter.Seq2[K, V]
func Transpose[K, V any](seq iter.Seq2[K, V]) iter.Seq2[V, K]
func OmitL[K, V any](seq iter.Seq2[K, V]) iter.Seq[K]
func OmitF[K, V any](seq iter.Seq2[K, V]) iter.Seq[V]
func Omit[K any](seq iter.Seq[K]) func(yield func() bool)
func Omit2[K, V any](seq iter.Seq2[K, V]) func(yield func() bool)
func Unify[K, V, U any](fn func(K, V) U, seq iter.Seq2[K, V]) iter.Seq[U]
func Divide[K, V, U any](fn func(U) (K, V), seq iter.Seq[U]) iter.Seq2[K, V]
func WindowSeq[V any](n int, seq iter.Seq[V]) iter.Seq[iter.Seq[V]]

Collectors: functions that collect data from iterators and convert to other data.

func AppendBytes(b []byte, seq iter.Seq[[]byte]) []byte
func ChanSend[V any](ctx context.Context, c chan<- V, seq iter.Seq[V]) (v V, sentAll bool)
func Every[V any](fn func(V) bool, seq iter.Seq[V]) bool
func Every2[K, V any](fn func(K, V) bool, seq iter.Seq2[K, V]) bool
func Any[V any](fn func(V) bool, seq iter.Seq[V]) bool
func Any2[K, V any](fn func(K, V) bool, seq iter.Seq2[K, V]) bool
func Find[V comparable](v V, seq iter.Seq[V]) (V, int)
func FindFunc[V any](f func(V) bool, seq iter.Seq[V]) (V, int)
func Find2[K, V comparable](k K, v V, seq iter.Seq2[K, V]) (K, V, int)
func FindFunc2[K, V any](fn func(K, V) bool, seq iter.Seq2[K, V]) (K, V, int)
func FindLast[V comparable](v V, seq iter.Seq[V]) (found V, idx int)
func FindLastFunc[V any](fn func(V) bool, seq iter.Seq[V]) (found V, idx int)
func FindLast2[K, V comparable](k K, v V, seq iter.Seq2[K, V]) (foundK K, foundV V, idx int)
func FindLastFunc2[K, V any](fn func(K, V) bool, seq iter.Seq2[K, V]) (foundK K, foundV V, idx int)
func First[V any](seq iter.Seq[V]) (k V, ok bool)
func First2[K, V any](seq iter.Seq2[K, V]) (k K, v V, ok bool)
func Last[V any](seq iter.Seq[V]) (v V, ok bool)
func Last2[K, V any](seq iter.Seq2[K, V]) (k K, v V, ok bool)
func ForEach[V any](fn func(V), seq iter.Seq[V])
func ForEach2[K, V any](fn func(K, V), seq iter.Seq2[K, V])
func ForEachGo[V any, G GoGroup](ctx context.Context, g G, fn func(context.Context, V) error, seq iter.Seq[V]) error
func ForEachGo2[K, V any, G GoGroup](ctx context.Context, g G, fn func(context.Context, K, V) error, seq iter.Seq2[K, V]) error
func Discard[V any](seq iter.Seq[V])
func Discard2[K, V any](seq iter.Seq2[K, V])
func TryFind[V any](f func(V) bool, seq iter.Seq2[V, error]) (v V, idx int, err error)
func TryForEach[V any](f func(V), seq iter.Seq2[V, error]) error
func TryReduce[Sum, V any](f func(Sum, V) Sum, sum Sum, seq iter.Seq2[V, error]) (Sum, error)
func TryCollect[E any](seq iter.Seq2[E, error]) ([]E, error)
func TryAppendSeq[S ~[]E, E any](s S, seq iter.Seq2[E, error]) (S, error)
func Write[V any](w io.Writer, marshaler func(v V, written int) ([]byte, error), seq iter.Seq[V]) (n int, er error)
func Write2[K, V any](w io.Writer, marshaler func(k K, v V, written int) ([]byte, error), seq iter.Seq2[K, V]) (n int, er error)
func Encode[V any, Enc interface{  Encode(v any) error }](enc Enc, seq iter.Seq[V]) error
func AppendSeq2[S ~[]KeyValue[K, V], K, V any](s S, seq iter.Seq2[K, V]) S
func Collect2[K, V any](seq iter.Seq2[K, V]) []KeyValue[K, V]
func Min[V cmp.Ordered](seq iter.Seq[V]) V
func MinFunc[V any](fn func(x, y V) int, seq iter.Seq[V]) V
func Max[V cmp.Ordered](seq iter.Seq[V]) V
func MaxFunc[V any](fn func(i, j V) int, seq iter.Seq[V]) V
func Nth[V any](n int, seq iter.Seq[V]) (v V, ok bool)
func Nth2[K, V any](n int, seq iter.Seq2[K, V]) (k K, v V, ok bool)
func ReduceGroup[K comparable, V, Sum any](reducer func(accumulator Sum, current V) Sum, initial Sum, seq iter.Seq2[K, V]) map[K]Sum
func InsertReduceGroup[Map ~map[K]Sum, K comparable, V, Sum any](m Map, reducer func(accumulator Sum, current V) Sum, initial Sum, seq iter.Seq2[K, V]) map[K]Sum
func StringsCollect(sizeHint int, seq iter.Seq[string]) string
func Sum[S Summable](seq iter.Seq[S]) S
func SumOf[V any, S Summable](selector func(ele V) S, seq iter.Seq[V]) S

hiter/iterable

Wrapper for iterable objects; heap, list, ring, slice, map, channel, etc.

All of them implement 1 or 2 of Iter() iter.Seq[V], Iter2() iter.Seq[K, V], IntoIter() iter.Seq[V] or IntoIter2() iter.Seq2[K, V]

package iterable // import "github.com/ngicks/go-iterator-helper/hiter/iterable"

type Chan[V any] struct{ ... }
type Heap[T any] struct{ ... }
type IndexAccessible[A hiter.Atter[T], T any] struct{ ... }
type JsonDecoder struct{ ... }
type ListAll[T any] struct{ ... }
type ListBackward[T any] struct{ ... }
type ListElementAll[T any] struct{ ... }
type ListElementBackward[T any] struct{ ... }
type MapAll[K comparable, V any] map[K]V
type MapSorted[K cmp.Ordered, V any] map[K]V
type MapSortedFunc[M ~map[K]V, K comparable, V any] struct{ ... }
type Nexter[T any, Nexter interface{ ... }] struct{ ... }
type Peekable[V any] struct{ ... }
    func NewPeekable[V any](seq iter.Seq[V]) *Peekable[V]
type Peekable2[K, V any] struct{ ... }
    func NewPeekable2[K, V any](seq iter.Seq2[K, V]) *Peekable2[K, V]
type Range[T hiter.Numeric] struct{ ... }
type Repeatable[V any] struct{ ... }
type Repeatable2[K, V any] struct{ ... }
type RepeatableFunc[V any] struct{ ... }
type RepeatableFunc2[K, V any] struct{ ... }
type Resumable[V any] struct{ ... }
    func NewResumable[V any](seq iter.Seq[V]) *Resumable[V]
type Resumable2[K, V any] struct{ ... }
    func NewResumable2[K, V any](seq iter.Seq2[K, V]) *Resumable2[K, V]
type RingAll[T any] struct{ ... }
type RingBackward[T any] struct{ ... }
type Scanner struct{ ... }
type SliceAll[E any] []E
type SliceBackward[E any] []E
type SqlRows[T any] struct{ ... }
type SyncMap[K comparable, V any] struct{ ... }
type XmlDecoder struct{ ... }

hiter/errbox

hiter/errbox defines an utility that wraps iter.Seq2[V, error] to iter.Seq[V] by remembering the first error encountered.

package errbox // import "github.com/ngicks/go-iterator-helper/hiter/errbox"

type Box[V any] struct{ ... }
    func New[V any](seq iter.Seq2[V, error]) *Box[V]
type JsonDecoder struct{ ... }
    func NewJsonDecoder(dec *json.Decoder) *JsonDecoder
type Nexter[V any] struct{ ... }
    func NewNexter[V any, N interface{ ... }](n N, scanner func(N) (V, error)) *Nexter[V]
type SqlRows[V any] struct{ ... }
    func NewSqlRows[V any](rows *sql.Rows, scanner func(*sql.Rows) (V, error)) *SqlRows[V]
type XmlDecoder struct{ ... }
    func NewXmlDecoder(dec *xml.Decoder) *XmlDecoder

hiter/async

hiter/async defines asynchronous adapters

package async // import "github.com/ngicks/go-iterator-helper/hiter/async"

func Chunk[V any](timeout time.Duration, n int, seq iter.Seq[V]) iter.Seq[[]V]
func Map[V1, V2 any](ctx context.Context, queueLimit int, workerLimit int, ...) iter.Seq2[V2, error]

hiter/sh

Some short hands for adapters. These are implemented only combining other components defined in this module (including x/exp/xiter).

package sh // import "github.com/ngicks/go-iterator-helper/hiter/sh"

package sh defines some short hand iterator adapters. sh only holds functions
which only combine other elements in this module.

func Cancellable[V any](n int, ctx context.Context, seq iter.Seq[V]) iter.Seq[V]
func Cancellable2[K, V any](n int, ctx context.Context, seq iter.Seq2[K, V]) iter.Seq2[K, V]
func Clone[S ~[]E, E any](seq iter.Seq[S]) iter.Seq[S]
func Clone2[S1 ~[]E1, S2 ~[]E2, E1, E2 any](seq iter.Seq2[S1, S2]) iter.Seq2[S1, S2]
func Collect[V any](seq iter.Seq[iter.Seq[V]]) iter.Seq[[]V]
func Collect2[K, V any](seq iter.Seq2[iter.Seq[K], iter.Seq[V]]) iter.Seq2[[]K, []V]
func HandleErr[V any](handle func(V, error) bool, seq iter.Seq2[V, error]) iter.Seq[V]
func Rng[Num intType](n Num) iter.Seq[Num]
func RngSourced[Num intType](n Num, src rand.Source) iter.Seq[Num]

x/exp/xiter

Those listed in #61898.

This package is vendored so that you can use it anywhere without copy-and-pasting everywhere. It is already frozen; no change will be made even when xiter proposal got some modification.

Documentation

Overview

This package only contains some example which did not fit to other packages. See other packages' godoc for usage.

Example (Async_chunk)
package main

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/ngicks/go-iterator-helper/hiter"
	"github.com/ngicks/go-iterator-helper/hiter/async"
)

func main() {
	var (
		wg sync.WaitGroup
		in = make(chan int)
	)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	wg.Add(1)
	go func() {
		defer wg.Done()
		ticker := time.NewTicker(500 * time.Nanosecond)
		defer ticker.Stop()
		_, _ = hiter.ChanSend(ctx, in, hiter.Tap(func(int) { <-ticker.C }, hiter.Range(0, 20)))
		close(in)
	}()

	first := true
	var count int
	for c := range async.Chunk(time.Microsecond, 5, hiter.Chan(ctx, in)) {
		count++
		for _, i := range c {
			if !first {
				fmt.Print(", ")
			}
			first = false
			fmt.Printf("%d", i)
		}
	}
	fmt.Println()
	wg.Wait()
	fmt.Printf("count > 0 = %t\n", count > 0)
}
Output:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
count > 0 = true
Example (Async_worker_channel)

Example async worker channel demonstrates usage of [hiter.Chan], [hiter.ChanSend]. It sends values from seq to worker running on separates goroutines. Workers work on values and then send results back to the main goroutine.

package main

import (
	"context"
	"fmt"
	"maps"
	"slices"
	"sync"

	"github.com/ngicks/go-iterator-helper/hiter"
	"github.com/ngicks/go-iterator-helper/hiter/iterable"
	"github.com/ngicks/go-iterator-helper/x/exp/xiter"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	works := []string{"foo", "bar", "baz"}

	in := make(chan string, 5)
	out := make(chan hiter.KeyValue[string, error])

	var wg sync.WaitGroup
	wg.Add(3)
	for range 3 {
		go func() {
			defer wg.Done()
			_, _ = hiter.ChanSend(
				ctx,
				out,
				xiter.Map(
					func(s string) hiter.KeyValue[string, error] {
						return hiter.KeyValue[string, error]{
							K: "✨" + s + "✨" + s + "✨",
							V: nil,
						}
					},
					hiter.Chan(ctx, in),
				),
			)
		}()
	}

	var wg2 sync.WaitGroup
	wg2.Add(1)
	go func() {
		defer wg2.Done()
		wg.Wait()
		close(out)
	}()

	_, _ = hiter.ChanSend(ctx, in, slices.Values(works))
	close(in)

	results := maps.Collect(hiter.FromKeyValue(hiter.Chan(ctx, out)))

	for result, err := range iterable.MapSorted[string, error](results).Iter2() {
		fmt.Printf("result = %s, err = %v\n", result, err)
	}

	wg2.Wait()

}
Output:

result = ✨bar✨bar✨, err = <nil>
result = ✨baz✨baz✨, err = <nil>
result = ✨foo✨foo✨, err = <nil>
Example (Async_worker_map)

Example async worker map demonstrates usage of async.Map. At the surface it is similar to [xiter.Map2]. Actually it calls mapper in separate goroutine. If you don't care about order of element, just send values to workers through a channel and send back through another channel.

package main

import (
	"context"
	"fmt"
	"slices"

	"github.com/ngicks/go-iterator-helper/hiter/async"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	works := []string{"foo", "bar", "baz"}

	// The order is kept.
	for result, err := range async.Map(
		ctx,
		/*queueLimit*/ 10,
		/*workerLimit*/ 5,
		/*mapper*/ func(ctx context.Context, s string) (string, error) {
			return "✨" + s + "✨" + s + "✨", nil
		},
		slices.Values(works),
	) {
		fmt.Printf("result = %s, err = %v\n", result, err)
	}
}
Output:

result = ✨foo✨foo✨, err = <nil>
result = ✨bar✨bar✨, err = <nil>
result = ✨baz✨baz✨, err = <nil>
Example (Async_worker_map_graceful_cancellation)
package main

import (
	"context"
	"fmt"
	"slices"

	"github.com/ngicks/go-iterator-helper/hiter/async"
	"github.com/ngicks/go-iterator-helper/hiter/sh"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	works := []string{"foo", "bar", "baz"}

	workerCtx, cancelWorker := context.WithCancel(context.Background())
	defer cancelWorker()

	for result, err := range async.Map(
		ctx,
		/*queueLimit*/ 1,
		/*workerLimit*/ 1,
		/*mapper*/ func(ctx context.Context, s string) (string, error) {
			combined, cancel := context.WithCancel(ctx)
			defer cancel()
			go func() {
				select {
				case <-ctx.Done():
				case <-combined.Done():
				case <-workerCtx.Done():
				}
				cancel()
			}()
			if combined.Err() != nil {
				return "", combined.Err()
			}
			return "✨" + s + "✨" + s + "✨", nil
		},
		sh.Cancellable(1, workerCtx, slices.Values(works)),
	) {
		fmt.Printf("result = %s, err = %v\n", result, err)
		cancelWorker()
	}
}
Output:

result = ✨foo✨foo✨, err = <nil>
result = ✨bar✨bar✨, err = <nil>
Example (Dec_enc_round_trip)
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"maps"
	"os"

	"github.com/ngicks/go-iterator-helper/hiter"
	"github.com/ngicks/go-iterator-helper/hiter/errbox"
	"github.com/ngicks/go-iterator-helper/x/exp/xiter"
)

func main() {
	src := []byte(`
	{"foo":"foo"}
	{"bar":"bar"}
	{"baz":"baz"}
	`)

	rawDec := json.NewDecoder(bytes.NewReader(src))
	dec := errbox.New(hiter.Decode[map[string]string](rawDec))
	defer dec.Stop()

	enc := json.NewEncoder(os.Stdout)

	err := hiter.Encode(
		enc,
		xiter.Map(
			func(m map[string]string) map[string]string {
				return maps.Collect(
					xiter.Map2(
						func(k, v string) (string, string) { return k + k, v + v },
						maps.All(m),
					),
				)
			},
			dec.IntoIter(),
		),
	)

	fmt.Printf("dec error = %v\n", dec.Err())
	fmt.Printf("enc error = %v\n", err)
}
Output:

{"foofoo":"foofoo"}
{"barbar":"barbar"}
{"bazbaz":"bazbaz"}
dec error = <nil>
enc error = <nil>
Example (Error_handle)

Example error handle demonstrates various way to handle error.

package main

import (
	"errors"
	"fmt"
	"slices"

	"github.com/ngicks/go-iterator-helper/hiter"
	"github.com/ngicks/go-iterator-helper/hiter/errbox"
	"github.com/ngicks/go-iterator-helper/hiter/sh"
	"github.com/ngicks/go-iterator-helper/x/exp/xiter"
)

func main() {
	var (
		errSample  = errors.New("sample")
		errSample2 = errors.New("sample2")
	)

	erroneous := hiter.Pairs(
		hiter.Range(0, 6),
		xiter.Concat(
			hiter.Repeat(error(nil), 2),
			hiter.Repeat(errSample2, 2),
			hiter.Once(errSample),
			hiter.Once(error(nil)),
		),
	)

	fmt.Println("TryFind:")
	v, idx, err := hiter.TryFind(func(i int) bool { return i > 0 }, erroneous)
	fmt.Printf("v = %d, idx = %d, err = %v\n", v, idx, err)
	v, idx, err = hiter.TryFind(func(i int) bool { return i > 5 }, erroneous)
	fmt.Printf("v = %d, idx = %d, err = %v\n", v, idx, err)
	fmt.Println()

	fmt.Println("TryForEach:")
	err = hiter.TryForEach(func(i int) { fmt.Printf("i = %d\n", i) }, erroneous)
	fmt.Printf("err = %v\n", err)
	fmt.Println()

	fmt.Println("TryReduce:")
	collected, err := hiter.TryReduce(func(c []int, i int) []int { return append(c, i) }, nil, erroneous)
	fmt.Printf("collected = %#v, err = %v\n", collected, err)
	fmt.Println()

	fmt.Println("HandleErr:")
	var handled error
	collected = slices.Collect(
		sh.HandleErr(
			func(i int, err error) bool {
				handled = err
				return errors.Is(err, errSample2)
			},
			erroneous,
		),
	)
	fmt.Printf("collected = %#v, err = %v\n", collected, handled)
	fmt.Println()

	fmt.Println("*errbox.Box:")
	box := errbox.New(erroneous)
	collected = slices.Collect(box.IntoIter())
	fmt.Printf("collected = %#v, err = %v\n", collected, box.Err())
	fmt.Println()
}
Output:

TryFind:
v = 1, idx = 1, err = <nil>
v = 0, idx = -1, err = sample2

TryForEach:
i = 0
i = 1
err = sample2

TryReduce:
collected = []int{0, 1}, err = sample2

HandleErr:
collected = []int{0, 1}, err = sample

*errbox.Box:
collected = []int{0, 1}, err = sample2
Example (Peek_and_continue)
package main

import (
	"fmt"

	"github.com/ngicks/go-iterator-helper/hiter"
	"github.com/ngicks/go-iterator-helper/hiter/iterable"
	"github.com/ngicks/go-iterator-helper/x/exp/xiter"
)

func main() {
	// iterator that yields 0 to 9 sequentially.
	src := hiter.Range(0, 10)

	fmt.Println("It replays data if break-ed and resumed.")

	count := 3
	first := true
	for v := range src {
		count--
		if count < 0 {
			break
		}
		if !first {
			fmt.Print(", ")
		}
		first = false
		fmt.Printf("%d", v)
	}
	fmt.Println()
	fmt.Println("break and resume")
	first = true
	for v := range xiter.Limit(src, 3) {
		if !first {
			fmt.Print(", ")
		}
		first = false
		fmt.Printf("%d", v)
	}
	fmt.Print("\n\n")

	fmt.Println("converting it to be resumable.")
	resumable := iterable.NewResumable(src)

	v0, _ := hiter.First(resumable.IntoIter())
	fmt.Printf("first:  %d\n", v0)
	v1, _ := hiter.First(resumable.IntoIter())
	fmt.Printf("second: %d\n", v1)

	fmt.Println()
	fmt.Println("reconnect them to whole iterator.")
	first = true
	for v := range xiter.Concat(hiter.Once(v0), hiter.Once(v1), resumable.IntoIter()) {
		if !first {
			fmt.Print(", ")
		}
		first = false
		fmt.Printf("%d", v)
	}
	fmt.Println()

	fmt.Println("\nYou can achieve above also with iterable.Peekable")
	peekable := iterable.NewPeekable(src)
	fmt.Printf("%#v\n", peekable.Peek(5))
	first = true
	for v := range peekable.IntoIter() {
		if !first {
			fmt.Print(", ")
		}
		first = false
		fmt.Printf("%d", v)
	}
	fmt.Println()

}
Output:

It replays data if break-ed and resumed.
0, 1, 2
break and resume
0, 1, 2

converting it to be resumable.
first:  0
second: 1

reconnect them to whole iterator.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9

You can achieve above also with iterable.Peekable
[]int{0, 1, 2, 3, 4}
0, 1, 2, 3, 4, 5, 6, 7, 8, 9

Directories

Path Synopsis
sh
package sh defines some short hand iterator adapters.
package sh defines some short hand iterator adapters.
internal
cmd/fill_readme command
x
exp/xiter
Code copied from
Code copied from

Jump to

Keyboard shortcuts

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