hiter

package
v0.0.13 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2024 License: MIT Imports: 18 Imported by: 13

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Alternate

func Alternate[V any](seqs ...iter.Seq[V]) iter.Seq[V]

Alternate returns an iterator that yields alternatively each seq. The first exhausted seq stops the iterator.

Example
package main

import (
	"fmt"

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

func main() {
	tick := hiter.Repeat("tick", 3)
	tac := hiter.Repeat("tac", 3)

	for msg := range hiter.Alternate(tick, tac) {
		fmt.Printf("%s ", msg)
	}
	fmt.Printf("ooooohhhhh...\n")
}
Output:

tick tac tick tac tick tac ooooohhhhh...

func Alternate2

func Alternate2[K, V any](seqs ...iter.Seq2[K, V]) iter.Seq2[K, V]

Alternate returns an iterator that yields alternatively each seq. The first exhausted seq stops the iterator.

func AppendBytes added in v0.0.13

func AppendBytes(b []byte, seq iter.Seq[[]byte]) []byte

AppendBytes appends byte slices from seq to b, and returns the extended slice.

func AppendSeq2

func AppendSeq2[S ~[]KeyValue[K, V], K, V any](s S, seq iter.Seq2[K, V]) S

AppendSeq2 appends the values from seq to the KeyValue slice and returns the extended slice.

func Chan

func Chan[V any](ctx context.Context, ch <-chan V) iter.Seq[V]

Chan returns an iterator over ch. Either cancelling ctx or closing ch stops iteration.

func ChanSend added in v0.0.8

func ChanSend[V any](ctx context.Context, c chan<- V, seq iter.Seq[V]) (v V, sentAll bool)

ChanSend sends values from seq to c. It unblocks after either ctx is cancelled or all values from seq are consumed. sentAll is true only when all values are sent to c. Otherwise sentAll is false and v is the value that is being blocked on sending to the channel.

Example
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

var (
	in, out = make(chan string), make(chan string)
	wg      sync.WaitGroup
)

wg.Add(1)
go func() {
	defer wg.Done()
	hiter.ChanSend(ctx, in, hiter.Repeat("hey", 3))
}()

for range 3 {
	wg.Add(1)
	go func() {
		defer wg.Done()
		// super duper heavy tasks
		_, _ = hiter.ChanSend(
			ctx,
			out,
			hiter.Tap(
				func(_ string) {
					// sleep for random duration.
					// Ensuring moderate task distribution among workers(goroutines.)
					time.Sleep(rand.N[time.Duration](100))
				},
				xiter.Map(
					func(s string) string { return "✨" + s + "✨" },
					hiter.Chan(ctx, in),
				),
			),
		)
	}()
}

for i, decorated := range hiter.Enumerate(hiter.Chan(ctx, out)) {
	fmt.Printf("%s\n", decorated)
	if i == 2 {
		cancel()
	}
}
wg.Wait()
Output:

✨hey✨
✨hey✨
✨hey✨

func Compact added in v0.0.11

func Compact[V comparable](seq iter.Seq[V]) iter.Seq[V]

Compact skips consecutive runs of equal elements from seq. The returned iterator is pure and stateless as long as seq is so.

Example
package main

import (
	"fmt"

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

func main() {
	m := xiter.Merge(
		xiter.Map(func(i int) int { return 2 * i }, hiter.Range(1, 11)),
		xiter.Map(func(i int) int { return 1 << i }, hiter.Range(1, 11)),
	)

	first := true
	for i := range hiter.Compact(m) {
		if !first {
			fmt.Printf(", ")
		}
		fmt.Printf("%d", i)
		first = false
	}
	fmt.Println()
}
Output:

2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 32, 64, 128, 256, 512, 1024

func Compact2 added in v0.0.11

func Compact2[K, V comparable](seq iter.Seq2[K, V]) iter.Seq2[K, V]

Compact2 skips consecutive runs of equal k-v pairs from seq. The returned iterator is pure and stateless as long as seq is so.

func CompactFunc added in v0.0.11

func CompactFunc[V any](eq func(i, j V) bool, seq iter.Seq[V]) iter.Seq[V]

CompactFunc is like Compact but uses an equality function to compare elements. For runs of elements that compare equal, CompactFunc keeps the first one.

func CompactFunc2 added in v0.0.11

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]

CompactFunc2 is like Compact2 but uses an equality function to compare elements. For runs of elements that compare equal, CompactFunc2 keeps the first one.

Example
package main

import (
	"fmt"
	"slices"

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

func main() {
	type example struct {
		Key  string
		Data string
	}
	for i, v := range hiter.CompactFunc2(
		func(i1 int, v1 example, i2 int, v2 example) bool { return v1.Key == v2.Key },
		hiter.Enumerate(slices.Values([]example{
			{"foo", "yay"}, {"foo", "nay"}, {"foo", "mah"},
			{"bar", "yay"},
			{"baz", "yay"}, {"baz", "nay"},
		})),
	) {
		fmt.Printf("%d: %v\n", i, v)
	}
}
Output:

0: {foo yay}
3: {bar yay}
4: {baz yay}

func Decode added in v0.0.13

func Decode[V any, Dec interface{ Decode(any) error }](dec Dec) iter.Seq2[V, error]

func Decorate

func Decorate[V any](prepend, append Iterable[V], seq iter.Seq[V]) iter.Seq[V]

Decorate decorates seq by prepend and append, by yielding additional elements before and after seq yields. Both prepend and append are allowed to be nil; only non-nil Iterable is used as decoration.

Example
package main

import (
	"fmt"
	"sync/atomic"

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

func main() {
	src := "foo bar baz"
	var num atomic.Int32
	numListTitle := iterable.RepeatableFunc[string]{
		FnV: func() string { return fmt.Sprintf("%d. ", num.Add(1)) },
		N:   1,
	}
	m := hiter.StringsCollect(
		9+((2 /*num*/ +2 /*. */ +1 /* */)*3),
		hiter.SkipLast(
			1,
			hiter.Decorate(
				numListTitle,
				iterable.Repeatable[string]{V: " ", N: 1},
				hiter.StringsSplitFunc(src, -1, hiter.StringsCutWord),
			),
		),
	)
	fmt.Printf("%s\n", m)
}
Output:

1. foo 2. bar 3. baz

func Decorate2

func Decorate2[K, V any](prepend, append Iterable2[K, V], seq iter.Seq2[K, V]) iter.Seq2[K, V]

Decorate2 decorates seq by prepend and append, by yielding additional elements before and after seq yields. Both prepend and append are allowed to be nil; only non-nil Iterable2 is used as decoration.

func Encode added in v0.0.13

func Encode[V any, Enc interface{ Encode(v any) error }](enc Enc, seq iter.Seq[V]) error

func Enumerate

func Enumerate[T any](seq iter.Seq[T]) iter.Seq2[int, T]

Enumerate adds 0-indexed integer counts to every values from seq.

func Find added in v0.0.13

func Find[V comparable](v V, seq iter.Seq[V]) (V, int)

Find iterates over seq and the first value equals to v with its count. The count is 0-indexed and -1 if seq does not have value equals v.

func Find2 added in v0.0.13

func Find2[K, V comparable](k K, v V, seq iter.Seq2[K, V]) (K, V, int)

Find2 iterates over seq and the first k-v pair equals to k and v with its count. The count is 0-indexed and -1 if seq does not have k-v pairs equals the input.

func FindFunc added in v0.0.13

func FindFunc[V any](f func(V) bool, seq iter.Seq[V]) (V, int)

FindFunc iterates over seq and the first value satisfying fn(v) with its count. The count is 0-indexed and -1 if seq does not have value that satisfies fn.

func FindFunc2 added in v0.0.13

func FindFunc2[K, V any](fn func(K, V) bool, seq iter.Seq2[K, V]) (K, V, int)

FindFunc2 iterates over seq and the first k-v pair satisfying fn(v) with its count. The count is 0-indexed and -1 if seq does not have k-v pairs that satisfies fn.

func FindLast added in v0.0.13

func FindLast[V comparable](v V, seq iter.Seq[V]) (found V, idx int)

FindLast is like Find but returns the final occurrence of v. Unlike Find, FindLast always fully consumes seq.

func FindLast2 added in v0.0.13

func FindLast2[K, V comparable](k K, v V, seq iter.Seq2[K, V]) (foundK K, foundV V, idx int)

FindLast2 is like Find2 but returns the final occurrence of k-v pair.

func FindLastFunc added in v0.0.13

func FindLastFunc[V any](fn func(V) bool, seq iter.Seq[V]) (found V, idx int)

FindLastFunc is like FindFunc but returns the final occurrence of v.

func FindLastFunc2 added in v0.0.13

func FindLastFunc2[K, V any](fn func(K, V) bool, seq iter.Seq2[K, V]) (foundK K, foundV V, idx int)

FindLastFunc2 is like FindFunc2 but returns the final occurrence of k-v pair.

func First added in v0.0.13

func First[V any](seq iter.Seq[V]) (k V, ok bool)

First returns the first value from seq. ok is false if seq yields no value.

func First2 added in v0.0.13

func First2[K, V any](seq iter.Seq2[K, V]) (k K, v V, ok bool)

First2 returns the first key-value pair from seq. ok is false if seq yields no pair.

func Flatten

func Flatten[S ~[]E, E any](seq iter.Seq[S]) iter.Seq[E]

Flatten returns an iterator over slices yielded from seq.

Example
package main

import (
	"fmt"
	"slices"

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

func main() {
	yayyay := []string{"yay", "yay", "yay"}
	wah := []string{"wah", "wah"}
	first := true
	for s := range hiter.Flatten(xiter.Concat(slices.Values([][]string{yayyay}), slices.Values([][]string{wah}))) {
		if !first {
			fmt.Print(" ")
		}
		fmt.Print(s)
		first = false
	}
}
Output:

yay yay yay wah wah

func FlattenF

func FlattenF[S1 ~[]E1, E1 any, E2 any](seq iter.Seq2[S1, E2]) iter.Seq2[E1, E2]

FlattenF returns an iterator over pairs of slice and non-slice. While iterating over slices, the latter part of pair is repeated until the slice is fully consumed.

Example
package main

import (
	"fmt"
	"slices"

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

func main() {
	yayyay := []string{"yay", "yay", "yay"}
	ohYah := []string{"oh", "yah"}
	first := true
	for k, v := range hiter.FlattenF(hiter.Pairs(hiter.Repeat(yayyay, -1), slices.Values(ohYah))) {
		if !first {
			fmt.Print(" ")
		}
		fmt.Printf("{%s %s}", k, v)
		first = false
	}
}
Output:

{yay oh} {yay oh} {yay oh} {yay yah} {yay yah} {yay yah}

func FlattenL

func FlattenL[S2 ~[]E2, E1 any, E2 any](seq iter.Seq2[E1, S2]) iter.Seq2[E1, E2]

FlattenL returns an iterator over pairs of non-slice and slice. While iterating over slices, the former part of pair is repeated until the slice is fully consumed.

Example
package main

import (
	"fmt"
	"slices"

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

func main() {
	yayyay := []string{"yay", "yay", "yay"}
	ohYah := []string{"oh", "yah"}
	first := true
	for k, v := range hiter.FlattenL(hiter.Pairs(slices.Values(yayyay), hiter.Repeat(ohYah, -1))) {
		if !first {
			fmt.Print(" ")
		}
		fmt.Printf("{%s %s}", k, v)
		first = false
	}
}
Output:

{yay oh} {yay yah} {yay oh} {yay yah} {yay oh} {yay yah}

func FromKeyValue added in v0.0.13

func FromKeyValue[K, V any](seq iter.Seq[KeyValue[K, V]]) iter.Seq2[K, V]

FromKeyValue unwraps iter.Seq[KeyValue[K, V]] into iter.Seq2[K, V] to counter-part, serving a counterpart for ToKeyValue.

In case values from seq needs to be sent through some data transfer mechanism that only allows data to be single value, like channels, some caller might decide to wrap values into KeyValue[K, V], maybe by ToKeyValue. If target helpers only accept iter.Seq2[K, V], then FromKeyValues is useful.

func Heap

func Heap[V any](h heap.Interface) iter.Seq[V]

Heap returns an iterator over heap.Interface. Consuming iter.Seq[V] also consumes h. To avoid this, callers must clone input h before passing to Heap.

Example
package main

import (
	"cmp"
	"container/heap"
	"fmt"

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

var _ heap.Interface = (*sliceHeap)(nil)

type sliceHeap []int

func (s *sliceHeap) Len() int           { return len(*s) }
func (s *sliceHeap) Less(i, j int) bool { return cmp.Less((*s)[i], (*s)[j]) }
func (s *sliceHeap) Swap(i, j int)      { (*s)[i], (*s)[j] = (*s)[j], (*s)[i] }
func (s *sliceHeap) Push(x any)         { (*s) = append((*s), x.(int)) }
func (s *sliceHeap) Pop() any {
	p := (*s)[len(*s)-1]
	// Zeroing out the removed part of slice.
	// This does nothing for types like int.
	// If the type is pointer or struct that contains pointer,
	// zeroing out lets GC clean up now-unused elements.
	(*s)[len(*s)-1] = 0
	*s = (*s)[:len(*s)-1]
	return p
}

func main() {
	h := &sliceHeap{0, 6, 1, 3, 2, 8, 210, 3, 7, 9, 2, 1, 54, 7}
	heap.Init(h)

	for num := range xiter.Limit(hiter.Heap[int](h), 5) {
		fmt.Printf("%d, ", num)
	}

	fmt.Println("...stopped here. and...")

	for num := range hiter.Heap[int](h) {
		fmt.Printf("%d, ", num)
		if h.Len() == 1 {
			break
		}
	}

	for num := range hiter.Heap[int](h) {
		fmt.Printf("%d\n", num)
	}

}
Output:

0, 1, 1, 2, 2, ...stopped here. and...
3, 3, 6, 7, 7, 8, 9, 54, 210

func IndexAccessible added in v0.0.6

func IndexAccessible[A Atter[T], T any](a A, indices iter.Seq[int]) iter.Seq2[int, T]

IndexAccessible returns an iterator over indices and values of a associated to the indices. If indices generates an out-of-range index, the behavior is not defined and may differs among Atter implementations.

Example
package main

import (
	"fmt"

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

type atSliceStr []string

func (a atSliceStr) At(i int) string {
	return a[i]
}

var atSliceSrc = atSliceStr{
	"foo", "bar", "baz",
	"qux", "quux", "corge",
	"grault", "garply", "waldo",
	"fred", "plugh", "xyzzy",
	"thud",
}

func main() {
	for i, s := range hiter.IndexAccessible(atSliceSrc, hiter.Range(3, 7)) {
		fmt.Printf("%d: %s\n", i, s)
	}
}
Output:

3: qux
4: quux
5: corge
6: grault

func JsonDecoder added in v0.0.6

func JsonDecoder(dec *json.Decoder) iter.Seq2[json.Token, error]

JsonDecoder returns an iterator over json tokens. The first non-nil error encountered stops iteration after yielding it. io.EOF is excluded from result.

func Last added in v0.0.13

func Last[V any](seq iter.Seq[V]) (v V, ok bool)

Last returns the last value from seq. ok is false if seq yields no value.

func Last2 added in v0.0.13

func Last2[K, V any](seq iter.Seq2[K, V]) (k K, v V, ok bool)

Last2 returns the last key-value pair from seq. ok is false if seq yields no pair.

func LimitAfter added in v0.0.13

func LimitAfter[V any](f func(V) bool, seq iter.Seq[V]) iter.Seq[V]

LimitAfter is like LimitUntil but also yields the first value dissatisfying f(v).

func LimitAfter2 added in v0.0.13

func LimitAfter2[K, V any](f func(K, V) bool, seq iter.Seq2[K, V]) iter.Seq2[K, V]

LimitAfter2 is like LimitUntil2 but also yields the first pair dissatisfying f(k, v).

func LimitUntil

func LimitUntil[V any](f func(V) bool, seq iter.Seq[V]) iter.Seq[V]

LimitUntil returns an iterator over seq that yields until f returns false.

func LimitUntil2

func LimitUntil2[K, V any](f func(K, V) bool, seq iter.Seq2[K, V]) iter.Seq2[K, V]

LimitUntil2 returns an iterator over seq that yields until f returns false.

func ListAll

func ListAll[V any](l *list.List) iter.Seq[V]

ListAll returns an iterator over all element of l starting from l.Front(). ListAll assumes Values of all element are type V. If other than that or nil, the returned iterator may panic on invocation.

Example
package main

import (
	"container/list"
	"fmt"
	"slices"

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

func main() {
	l := list.New()

	for _, s := range []string{"foo", "bar", "baz"} {
		l.PushBack(s)
	}

	fmt.Printf("all:              %#v\n", slices.Collect(hiter.ListAll[string](l)))
	fmt.Printf("backward:         %#v\n", slices.Collect(hiter.ListBackward[string](l)))
	fmt.Printf("element all:      %#v\n", slices.Collect(hiter.ListElementAll[string](l.Front().Next())))
	fmt.Printf("element backward: %#v\n", slices.Collect(hiter.ListElementBackward[string](l.Front().Next())))
}
Output:

all:              []string{"foo", "bar", "baz"}
backward:         []string{"baz", "bar", "foo"}
element all:      []string{"bar", "baz"}
element backward: []string{"bar", "foo"}

func ListBackward

func ListBackward[V any](l *list.List) iter.Seq[V]

ListBackward returns an iterator over all element of l starting from l.Back(). ListBackward assumes Values of all element are type V. If other than that or nil, the returned iterator may panic on invocation.

func ListElementAll added in v0.0.4

func ListElementAll[V any](ele *list.Element) iter.Seq[V]

ListElementAll returns an iterator over from ele to end of the list. ListElementAll assumes Values of all element are type V. If other than that or nil, the returned iterator may panic on invocation.

func ListElementBackward added in v0.0.4

func ListElementBackward[V any](ele *list.Element) iter.Seq[V]

ListElementBackward returns an iterator over from ele to start of the list. ListElementBackward assumes Values of all element are type V. If other than that or nil, the returned iterator may panic on invocation.

func MergeSort added in v0.0.11

func MergeSort[S ~[]T, T cmp.Ordered](m S) iter.Seq[T]

MergeSort implements merge sort algorithm. Basically you should use []T -> []T implementation since this allocates a lot more. MergeSort is worthy only when T is big structs and you only need one element at time, not a whole slice.

Example
package main

import (
	"cmp"
	"fmt"
	"iter"
	"math/rand/v2"
	"slices"

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

// avoiding xter dep
func limit[V any](seq iter.Seq[V], n int) iter.Seq[V] {
	return func(yield func(V) bool) {
		if n <= 0 {
			return
		}
		for v := range seq {
			if !yield(v) {
				return
			}
			if n--; n <= 0 {
				break
			}
		}
	}
}

func mergeSortFunc[S ~[]T, T any](m S, cmp func(l, r T) int) S {
	if len(m) <= 1 {
		return m
	}
	left, right := m[:len(m)/2], m[len(m)/2:]
	left = mergeSortFunc(left, cmp)
	right = mergeSortFunc(right, cmp)
	return mergeFunc(left, right, cmp)
}

func mergeFunc[S ~[]T, T any](l, r S, cmp func(l, r T) int) S {
	m := make(S, len(l)+len(r))
	var i int
	for i = 0; len(l) > 0 && len(r) > 0; i++ {
		if cmp(l[0], r[0]) < 0 {
			m[i] = l[0]
			l = l[1:]
		} else {
			m[i] = r[0]
			r = r[1:]
		}
	}
	for _, t := range l {
		m[i] = t
		i++
	}
	for _, t := range r {
		m[i] = t
		i++
	}
	return m
}

var _ hiter.SliceLike[any] = sliceAdapter[any]{}

type sliceAdapter[T any] []T

func (s sliceAdapter[T]) At(i int) T {
	return s[i]
}

func (s sliceAdapter[T]) Len() int {
	return len(s)
}

func main() {
	rng := hiter.RepeatFunc(func() int { return rand.N(20) }, -1)
	fmt.Printf("merge sort: %t\n",
		slices.IsSorted(mergeSortFunc(slices.Collect(limit(rng, 10)), cmp.Compare)),
	)
	fmt.Printf(
		"merge sort iter: %t\n",
		slices.IsSorted(
			slices.Collect(
				hiter.MergeSort(slices.Collect(limit(rng, 10))),
			),
		),
	)
	fmt.Printf(
		"merge sort atter: %t\n",
		slices.IsSorted(
			slices.Collect(
				hiter.MergeSortSliceLike(sliceAdapter[int](slices.Collect(limit(rng, 10)))),
			),
		),
	)
	fmt.Printf(
		"merge sort atter logically concatenated: %t\n",
		slices.IsSorted(
			slices.Collect(
				hiter.MergeSortSliceLike(
					hiter.ConcatSliceLike(
						slices.Collect(
							xiter.Map(
								func(i int) hiter.SliceLike[int] {
									return sliceAdapter[int](slices.Collect(limit(rng, i)))
								},
								xiter.Limit(rng, 5),
							),
						)...,
					),
				),
			),
		),
	)
}
Output:

merge sort: true
merge sort iter: true
merge sort atter: true
merge sort atter logically concatenated: true

func MergeSortFunc added in v0.0.11

func MergeSortFunc[S ~[]T, T any](m S, cmp func(l, r T) int) iter.Seq[T]

MergeSortFunc is like MergeSort but uses comparison function.

func MergeSortSliceLike added in v0.0.11

func MergeSortSliceLike[S SliceLike[T], T cmp.Ordered](s S) iter.Seq[T]

MergeSortSliceLike is like MergeSort that uses SliceLike interface instead of []T. This implementation is quite experimental. Basically you do not want to use this since it is much, much less performant.

func MergeSortSliceLikeFunc added in v0.0.11

func MergeSortSliceLikeFunc[S SliceLike[T], T any](s S, cmp func(l, r T) int) iter.Seq[T]

MergeSortSliceLikeFunc is like MergeSortSliceLike but uses comparison function instead.

func Omit

func Omit[K any](seq iter.Seq[K]) func(yield func() bool)

Omit returns an iterator over seq but drops data seq yields.

func Omit2

func Omit2[K, V any](seq iter.Seq2[K, V]) func(yield func() bool)

Omit2 returns an iterator over seq but drops data seq yields.

func OmitF

func OmitF[K, V any](seq iter.Seq2[K, V]) iter.Seq[V]

OmitF drops former part of key-value pairs that seq generates.

func OmitL

func OmitL[K, V any](seq iter.Seq2[K, V]) iter.Seq[K]

OmitL drops latter part of key-value pairs that seq generates.

func Pairs

func Pairs[K, V any](seq1 iter.Seq[K], seq2 iter.Seq[V]) iter.Seq2[K, V]

Pairs combines seq1 and seq2 into an iterator over key-value pairs. If either stops, the returned iterator stops.

func Permutations added in v0.0.11

func Permutations[S ~[]E, E any](in S) iter.Seq[S]

Permutations returns an iterator that yields permutations of in. The returned iterator reorders in in-place. The caller should not retain in or slices from the iterator, Or should explicitly clone yielded values.

Example
package main

import (
	"fmt"

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

func main() {
	in := []int{1, 2, 3}
	for p := range hiter.Permutations(in) {
		fmt.Printf("%#v\n", p)
	}
}
Output:

[]int{1, 2, 3}
[]int{2, 1, 3}
[]int{3, 1, 2}
[]int{1, 3, 2}
[]int{2, 3, 1}
[]int{3, 2, 1}

func Range

func Range[T Numeric](start, end T) iter.Seq[T]

Range produces an iterator that yields sequential Numeric values in range [start, end). Values start from `start` and step toward `end`. At each step value is increased by 1 if start < end, otherwise decreased by 1.

Example (Char)
package main

import (
	"fmt"

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

func main() {
	fmt.Println(
		hiter.StringsCollect(
			27,
			xiter.Map(
				func(r rune) string {
					return string(r - ('a' - 'A'))
				},
				hiter.Range('a', 'z'+1),
			),
		),
	)
}
Output:

ABCDEFGHIJKLMNOPQRSTUVWXYZ
Example (Prevent_off_by_one)
package main

import (
	"fmt"

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

func main() {
	for i := range hiter.LimitUntil(
		func(i int) bool { return i < 50 },
		xiter.Map(
			func(i int) int { return i * 7 },
			hiter.Range(0, 10),
		),
	) {
		if i > 0 {
			fmt.Printf(" ")
		}
		fmt.Printf("%d", i)
	}
}
Output:

0 7 14 21 28 35 42 49

func ReduceGroup added in v0.0.11

func ReduceGroup[K comparable, V, Sum any](
	reducer func(accumulator Sum, current V) Sum,
	initial Sum,
	seq iter.Seq2[K, V],
) map[K]Sum

ReduceGroup sums up values from seq for every unique keys separately, then collects pairs of key and converted value into a new map and returns it. initial is passed once for every unique keys to reducer.

Example
package main

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

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

func main() {
	m1 := map[int]string{
		0: "foo",
		1: "bar",
		2: "baz",
	}
	m2 := map[int]string{
		0: "foo",
		2: "zab",
		3: "gooood",
	}

	reduced := hiter.ReduceGroup(
		func(sum []string, c string) []string { return append(sum, c) },
		nil,
		xiter.Concat2(maps.All(m1), maps.All(m2)),
	)

	bin, _ := json.MarshalIndent(reduced, "", "    ")

	fmt.Printf("%s\n", bin)
}
Output:

{
    "0": [
        "foo",
        "foo"
    ],
    "1": [
        "bar"
    ],
    "2": [
        "baz",
        "zab"
    ],
    "3": [
        "gooood"
    ]
}

func Repeat

func Repeat[V any](v V, n int) iter.Seq[V]

Repeat returns an iterator over v repeated n times. If n < 0, the returned iterator repeats forever.

func Repeat2

func Repeat2[K, V any](k K, v V, n int) iter.Seq2[K, V]

Repeat2 returns an iterator over the pair of k and v repeated n times. If n < 0, the returned iterator repeats forever.

func RepeatFunc added in v0.0.3

func RepeatFunc[V any](fnV func() V, n int) iter.Seq[V]

RepeatFunc returns an iterator that generates result from fnV n times. If n < 0, the returned iterator repeats forever.

func RepeatFunc2 added in v0.0.3

func RepeatFunc2[K, V any](fnK func() K, fnV func() V, n int) iter.Seq2[K, V]

RepeatFunc2 returns an iterator that generates result of fnK and fnV n times. If n < 0, the returned iterator repeats forever.

func RingAll

func RingAll[V any](r *ring.Ring) iter.Seq[V]

Ring returns an iterator over r. The returned iterator generates data assuming Values of all ring elements are type V. It yields r.Value traversing by consecutively calling Next, and stops when it finds r again. Removing r from the ring after it started iteration may make it iterate forever.

Example
package main

import (
	"container/ring"
	"fmt"
	"slices"

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

func main() {
	ringBufSize := 5
	r := ring.New(ringBufSize)

	for i := range ringBufSize {
		r.Value = i
		r = r.Next()
	}

	fmt.Printf("all:      %#v\n", slices.Collect(hiter.RingAll[int](r)))
	fmt.Printf("backward: %#v\n", slices.Collect(hiter.RingBackward[int](r.Prev())))

	// Now, we'll demonstrate buffer like usage of ring.

	pushBack := func(v int) {
		r.Value = v
		r = r.Next()
	}

	pushBack(12)
	pushBack(5)
	fmt.Printf("1:        %#v\n", slices.Collect(hiter.RingAll[int](r)))

	pushBack(8)
	fmt.Printf("2:        %#v\n", slices.Collect(hiter.RingAll[int](r)))

	pushBack(36)
	fmt.Printf("3:        %#v\n", slices.Collect(hiter.RingAll[int](r)))

}
Output:

all:      []int{0, 1, 2, 3, 4}
backward: []int{4, 3, 2, 1, 0}
1:        []int{2, 3, 4, 12, 5}
2:        []int{3, 4, 12, 5, 8}
3:        []int{4, 12, 5, 8, 36}
Example (Moving_average)

ExampleRingAll_moving_average demonstrates buffer-like usage of RingAll.

package main

import (
	"container/ring"
	"fmt"
	"iter"
	"slices"

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

func dynamicWindow[V any](size int, seq iter.Seq[V]) iter.Seq[iter.Seq[V]] {
	return func(yield func(iter.Seq[V]) bool) {
		var (
			full     = false
			bufStart = ring.New(size)
		)
		buf := bufStart
		for v := range seq {
			if !full {
				buf.Value = v
				buf = buf.Next()
				if buf == bufStart {
					full = true
					if !yield(hiter.RingAll[V](buf)) {
						return
					}
				}
				continue
			}
			buf.Value = v
			buf = buf.Next()
			if !yield(hiter.RingAll[V](buf)) {
				return
			}
		}
	}
}

func main() {
	windowSize := 5
	src := slices.Values([]int{1, 0, 1, 0, 1, 0, 5, 3, 2, 3, 4, 6, 5, 3, 6, 7, 7, 8, 9, 5, 7, 7, 8})

	first := true
	for avg := range xiter.Map(
		func(s iter.Seq[int]) float64 {
			return float64(hiter.Sum(s)) / float64(windowSize)
		},
		dynamicWindow(windowSize, src),
	) {
		if !first {
			fmt.Print(", ")
		}
		fmt.Printf("%02.1f", avg)
		first = false
	}
	fmt.Println()
}
Output:

0.6, 0.4, 1.4, 1.8, 2.2, 2.6, 3.4, 3.6, 4.0, 4.2, 4.8, 5.4, 5.6, 6.2, 7.4, 7.2, 7.2, 7.2, 7.2

func RingBackward

func RingBackward[V any](r *ring.Ring) iter.Seq[V]

RingBackward returns an iterator over r. The returned iterator generates data assuming Values of all ring elements are type V. It yields r.Value traversing by consecutively calling Prev, and stops when it finds r again. Removing r from the ring after it started iteration may make it iterate forever.

func RunningReduce added in v0.0.11

func RunningReduce[V, Sum any](
	reducer func(accumulator Sum, current V, i int) Sum,
	initial Sum,
	seq iter.Seq[V],
) iter.Seq[Sum]

RunningReduce returns an iterator over every intermediate reducer results.

Example
package main

import (
	"fmt"
	"math"

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

func main() {
	for i, r := range hiter.Enumerate(
		hiter.RunningReduce(
			func(accum int64, next int64, idx int) int64 { return accum + next },
			int64(0),
			hiter.Range[int64](1, math.MaxInt64),
		),
	) {
		if i >= 5 {
			break
		}
		fmt.Printf("%d\n", r)
	}
}
Output:

1
3
6
10
15

func Scan

func Scan(scanner *bufio.Scanner) iter.Seq[string]

Scanner wraps scanner with an iterator over scanned text. The caller should check bufio.Scanner.Err after the returned iterator stops to see if it has been stopped for an error.

func Single added in v0.0.13

func Single[V any](v V) iter.Seq[V]

func Single2 added in v0.0.13

func Single2[K, V any](k K, v V) iter.Seq2[K, V]

func Skip

func Skip[V any](n int, seq iter.Seq[V]) iter.Seq[V]

Skip returns an iterator over seq that skips n elements from seq.

func Skip2

func Skip2[K, V any](n int, seq iter.Seq2[K, V]) iter.Seq2[K, V]

Skip2 returns an iterator over seq that skips n key-value pairs from seq.

func SkipLast added in v0.0.5

func SkipLast[V any](n int, seq iter.Seq[V]) iter.Seq[V]

SkipLast returns an iterator over seq that skips last n elements of seq.

Example
package main

import (
	"fmt"

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

func main() {
	fmt.Print("Without SkipLast: ")
	first := true
	for i := range hiter.Range(0, 10) {
		if !first {
			fmt.Print(", ")
		}
		fmt.Printf("%d", i)
		first = false
	}
	fmt.Println()
	fmt.Print("With SkipLast:    ")

	first = true
	for i := range hiter.SkipLast(5, hiter.Range(0, 10)) {
		if !first {
			fmt.Print(", ")
		}
		fmt.Printf("%d", i)
		first = false
	}
	fmt.Println()
}
Output:

Without SkipLast: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
With SkipLast:    0, 1, 2, 3, 4

func SkipLast2 added in v0.0.5

func SkipLast2[K, V any](n int, seq iter.Seq2[K, V]) iter.Seq2[K, V]

SkipLast2 returns an iterator over seq that skips last n key-value pairs of seq.

func SkipWhile

func SkipWhile[V any](f func(V) bool, seq iter.Seq[V]) iter.Seq[V]

SkipWhile returns an iterator over seq that skips elements until f returns false.

func SkipWhile2

func SkipWhile2[K, V any](f func(K, V) bool, seq iter.Seq2[K, V]) iter.Seq2[K, V]

SkipWhile2 returns an iterator over seq that skips key-value pairs until f returns false.

func SqlRows added in v0.0.9

func SqlRows[T any](r *sql.Rows, scanner func(*sql.Rows) (T, error)) iter.Seq2[T, error]

SqlRows returns an iterator over scanned rows from r. scanner will be called against *sql.Rows after each time *sql.Rows.Next returns true. It must either call *sql.Rows.Scan once per invocation or do nothing and return. If the scan result or *sql.Rows.Err returns a non-nil error, the iterator stops its iteration immediately after yielding the error.

func StringsChunk

func StringsChunk(s string, n int) iter.Seq[string]

StringsChunk returns an iterator over non overlapping sub strings of n bytes. Sub slicing may cut in mid of utf8 sequences.

func StringsCollect

func StringsCollect(sizeHint int, seq iter.Seq[string]) string

StringsCollect reduces seq to a single string. sizeHint hints size of internal buffer. Correctly sized sizeHint may reduce allocation.

func StringsCutNewLine

func StringsCutNewLine(s string) (tokUntil int, skipUntil int)

StringsCutNewLine is used with StringsSplitFunc. The input strings will be splitted at "\n". It also skips "\r" preceding "\n".

func StringsCutUpperCase

func StringsCutUpperCase(s string) (tokUntil int, skipUntil int)

StringsCutUpperCase is a split function for a StringsSplitFunc that splits "UpperCasedWords" into "Upper" "Cased" "Words"

func StringsCutWord added in v0.0.5

func StringsCutWord(s string) (tokUntil int, skipUntil int)

StringsCutWord is a split function for a StringsSplitFunc that returns each space-separated word of text, with surrounding spaces deleted. It will never return an empty string. The definition of space is set by unicode.IsSpace.

func StringsRuneChunk

func StringsRuneChunk(s string, n int) iter.Seq[string]

StringsRuneChunk returns an iterator over non overlapping sub strings of n utf8 characters.

func StringsSplitFunc

func StringsSplitFunc(s string, n int, splitFn StringsCutterFunc) iter.Seq[string]

StringsSplitFunc returns an iterator over sub string of s cut by splitFn. When n > 0, StringsSplitFunc cuts only n times and the returned iterator yields rest of string after n sub strings, if non empty. The sub strings from the iterator overlaps if splitFn decides so. splitFn is allowed to return negative offsets. In that case the returned iterator immediately yields rest of s and stops iteration.

func Sum added in v0.0.11

func Sum[S Summable](seq iter.Seq[S]) S

func SumOf added in v0.0.11

func SumOf[V any, S Summable](selector func(ele V) S, seq iter.Seq[V]) S

func SyncMap

func SyncMap[K, V any](m *sync.Map) iter.Seq2[K, V]

SyncMap returns an iterator over m. Breaking Seq2 may stop producing more data, however it might still be O(N).

func Tap added in v0.0.7

func Tap[V any](tap func(V), seq iter.Seq[V]) iter.Seq[V]

Tap returns an iterator over seq without any modification to values from seq. tap is called against every value from seq to observe values. For purpose of Tap, the tap callback function should not retain arguments.

Example
package main

import (
	"fmt"

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

func main() {
	for i := range hiter.Tap(
		func(i int) {
			fmt.Printf("observed: %d\n", i)
		},
		hiter.Range(1, 4),
	) {
		fmt.Printf("yielded:  %d\n", i)
	}
}
Output:

observed: 1
yielded:  1
observed: 2
yielded:  2
observed: 3
yielded:  3

func Tap2 added in v0.0.7

func Tap2[K, V any](tap func(K, V), seq iter.Seq2[K, V]) iter.Seq2[K, V]

Tap2 returns an iterator over seq without any modification to pairs from seq. tap is called against every pair from seq to observe pairs. For purpose of Tap, the tap callback function should not retain arguments.

func ToKeyValue added in v0.0.13

func ToKeyValue[K, V any](seq iter.Seq2[K, V]) iter.Seq[KeyValue[K, V]]

ToKeyValue converts iter.Seq2[K, V] into iter.Seq[KeyValue[K, V]]. This functions is particularly useful when sending values from iter.Seq2[K, V] through some data transfer mechanism that only allows data to be single value, e.g. channels.

func Transpose

func Transpose[K, V any](seq iter.Seq2[K, V]) iter.Seq2[V, K]

Transpose returns an iterator over seq that yields K and V reversed.

func Window

func Window[S ~[]E, E any](s S, n int) iter.Seq[S]

Window returns an iterator over overlapping sub-slices of n size (moving windows). n must be a positive non zero value. Values from the iterator are always slices of n size. The iterator yields nothing when it is not possible.

Example (Moving_average)
package main

import (
	"fmt"
	"slices"

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

func main() {
	src := []int{1, 0, 1, 0, 1, 0, 5, 3, 2, 3, 4, 6, 5, 3, 6, 7, 7, 8, 9, 5, 7, 7, 8}
	first := true
	for avg := range xiter.Map(
		func(s []int) float64 {
			return float64(hiter.Sum(slices.Values(s))) / float64(len(s))
		},
		hiter.Window(src, 5),
	) {
		if !first {
			fmt.Print(", ")
		}
		fmt.Printf("%02.1f", avg)
		first = false
	}
	fmt.Println()
}
Output:

0.6, 0.4, 1.4, 1.8, 2.2, 2.6, 3.4, 3.6, 4.0, 4.2, 4.8, 5.4, 5.6, 6.2, 7.4, 7.2, 7.2, 7.2, 7.2

func WindowSeq added in v0.0.13

func WindowSeq[V any](n int, seq iter.Seq[V]) iter.Seq[iter.Seq[V]]

WindowSeq allocates n sized buffer and fills it with values from seq in FIFO-manner. Once the buffer is full, the returned iterator yields iterator over buffered values each time value is yielded from the input seq.

n must be a positive non zero value. Each iterator yields exact n size of values. If seq yields less than n, the iterator yields nothing.

Example (Moving_average)
package main

import (
	"fmt"
	"iter"
	"slices"

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

func main() {
	src := []int{1, 0, 1, 0, 1, 0, 5, 3, 2, 3, 4, 6, 5, 3, 6, 7, 7, 8, 9, 5, 7, 7, 8}
	first := true
	for avg := range xiter.Map(
		func(s iter.Seq[int]) float64 {
			return float64(hiter.Sum(s)) / float64(5)
		},
		hiter.WindowSeq(5, slices.Values(src)),
	) {
		if !first {
			fmt.Print(", ")
		}
		fmt.Printf("%02.1f", avg)
		first = false
	}
	fmt.Println()
}
Output:

0.6, 0.4, 1.4, 1.8, 2.2, 2.6, 3.4, 3.6, 4.0, 4.2, 4.8, 5.4, 5.6, 6.2, 7.4, 7.2, 7.2, 7.2, 7.2

func Write added in v0.0.13

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 added in v0.0.13

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 XmlDecoder added in v0.0.6

func XmlDecoder(dec *xml.Decoder) iter.Seq2[xml.Token, error]

XmlDecoder returns an iterator over xml tokens. The first non-nil error encountered stops iteration after yielding it. io.EOF is excluded from result. The caller should call xml.CopyToken before going to next iteration if they need to retain tokens.

Types

type Atter added in v0.0.6

type Atter[T any] interface {
	At(i int) T
}

type FuncIterable

type FuncIterable[V any] func() iter.Seq[V]

func (FuncIterable[V]) IntoIter

func (f FuncIterable[V]) IntoIter() iter.Seq[V]

func (FuncIterable[V]) Iter

func (f FuncIterable[V]) Iter() iter.Seq[V]

type FuncIterable2

type FuncIterable2[K, V any] func() iter.Seq2[K, V]

func (FuncIterable2[K, V]) IntoIter2

func (f FuncIterable2[K, V]) IntoIter2() iter.Seq2[K, V]

func (FuncIterable2[K, V]) Iter2

func (f FuncIterable2[K, V]) Iter2() iter.Seq2[K, V]

type IntoIterable

type IntoIterable[V any] interface {
	IntoIter() iter.Seq[V]
}

IntoIterable wraps basic IntoIter2 method.

IntoIter might return non-pure / stateful iterators, which would also mutate internal state of implementation. Therefore calling the method or invoking the returned iterator multiple times might yield different data without replaying them.

type IntoIterable2

type IntoIterable2[K, V any] interface {
	IntoIter2() iter.Seq2[K, V]
}

IntoIterable2 wraps basic IntoIter2 method.

IntoIter2 might return non-pure / stateful iterators, which would also mutate internal state of implementation. Therefore calling the method or invoking the returned iterator multiple times might yield different data without replaying them.

type Iterable

type Iterable[V any] interface {
	Iter() iter.Seq[V]
}

Iterable wraps basic Iter method.

Iter should always return pure / stateless iterators, which always generates same set of data.

type Iterable2

type Iterable2[K, V any] interface {
	Iter2() iter.Seq2[K, V]
}

Iterable2 wraps basic Iter2 method.

Iter2 should always return pure / stateless iterators, which always generates same set of pairs.

type KeyValue

type KeyValue[K, V any] struct {
	K K
	V V
}

func Collect2

func Collect2[K, V any](seq iter.Seq2[K, V]) []KeyValue[K, V]

Collect2 collects values from seq into a new KeyValue slice and returns it.

type KeyValues

type KeyValues[K, V any] []KeyValue[K, V]

KeyValues adds the Iter2 method to slice of KeyValue-s.

Example

Useful for test data.

package main

import (
	"fmt"

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

func main() {
	kv := hiter.KeyValues[string, string]{{"foo", "bar"}, {"baz", "qux"}, {"quux", "corge"}}

	for k, v := range kv.Iter2() {
		fmt.Printf("%s:%s\n", k, v)
	}
}
Output:

foo:bar
baz:qux
quux:corge

func (KeyValues[K, V]) Iter2

func (v KeyValues[K, V]) Iter2() iter.Seq2[K, V]

type Numeric

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

type SliceLike added in v0.0.11

type SliceLike[T any] interface {
	Atter[T]
	Len() int
}

func ConcatSliceLike added in v0.0.11

func ConcatSliceLike[T any](s ...SliceLike[T]) SliceLike[T]

ConcatSliceLike logically concatenates SliceLike[T] implementations to form a single concatenated SliceLike[T].

type StringsCutterFunc

type StringsCutterFunc func(s string) (tokUntil, skipUntil int)

StringsCutterFunc is used with StringsSplitFunc to cut string from head. s[:tokUntil] is yielded through StringsSplitFunc. s[tokUntil:skipUntil] will be ignored.

type Summable added in v0.0.11

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

as per https://go.dev/ref/spec#arithmetic_operators

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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