Files
phishingclub/backend/vendor/github.com/enetx/g/seq.go
T
RonniSkansing 2028391c3c vendor backend
Signed-off-by: RonniSkansing <rskansing@gmail.com>
2026-09-16 23:17:35 +02:00

1960 lines
48 KiB
Go

package g
import (
"context"
"iter"
"reflect"
"slices"
"github.com/enetx/g/f"
"github.com/enetx/g/cmp"
"github.com/enetx/g/constraints"
)
// Seq is an iterator over sequences of individual values.
type Seq[V any] func(yield func(V) bool)
// SeqSlices is an iterator over slices of sequences of individual values.
//
// It is a distinct named type rather than Seq[[]V]: a named Seq[[]V] mentioned
// from Seq[V]'s method set (Chunks, Windows, ChunkBy, Combinations,
// Permutations) would be an instantiation cycle the compiler rejects.
type SeqSlices[V any] func(yield func([]V) bool)
// Range returns a Seq[T] yielding a sequence of integers of type T,
// starting at start, incrementing by step, and ending before stop (exclusive).
//
// - If step is omitted, it defaults to 1.
// - If step is 0, the sequence is empty.
// - If step does not move toward stop (e.g., positive step with start > stop),
// the sequence is empty.
//
// Examples:
// - Range(0, 5) yields [0, 1, 2, 3, 4]
// - Range(5, 0, -1) yields [5, 4, 3, 2, 1]
func Range[T constraints.Integer](start, stop T, step ...T) Seq[T] {
stepValue := T(1)
if len(step) > 0 {
stepValue = step[0]
}
return func(yield func(T) bool) {
if stepValue == 0 {
return
}
if stepValue > 0 {
for i := start; i < stop; i += stepValue {
if !yield(i) {
return
}
}
} else {
for i := start; i > stop; i += stepValue {
if !yield(i) {
return
}
}
}
}
}
// RangeInclusive returns a Seq[T] yielding a sequence of integers of type T,
// starting at start, incrementing by step, and ending at stop (inclusive).
//
// - If step is omitted, it defaults to 1.
// - If step is 0, the sequence is empty.
// - If step does not move toward stop (e.g., positive step with start > stop),
// the sequence is empty.
//
// Examples:
// - RangeInclusive(0, 5) yields [0, 1, 2, 3, 4, 5]
// - RangeInclusive(5, 0, -1) yields [5, 4, 3, 2, 1, 0]
func RangeInclusive[T constraints.Integer](start, stop T, step ...T) Seq[T] {
stepValue := T(1)
if len(step) > 0 {
stepValue = step[0]
}
return func(yield func(T) bool) {
if stepValue == 0 {
return
}
if stepValue > 0 {
for i := start; i <= stop; i += stepValue {
if !yield(i) {
return
}
}
} else {
for i := start; i >= stop; i += stepValue {
if !yield(i) {
return
}
}
}
}
}
// Pull converts the "push-style" iterator sequence seq
// into a "pull-style" iterator accessed by the two functions
// next and stop.
//
// Next returns the next value in the sequence
// and a boolean indicating whether the value is valid.
// When the sequence is over, next returns the zero V and false.
// It is valid to call next after reaching the end of the sequence
// or after calling stop. These calls will continue
// to return the zero V and false.
//
// Stop ends the iteration. It must be called when the caller is
// no longer interested in next values and next has not yet
// signaled that the sequence is over (with a false boolean return).
// It is valid to call stop multiple times and when next has
// already returned false.
//
// It is an error to call next or stop from multiple goroutines
// simultaneously.
func (seq Seq[V]) Pull() (func() (V, bool), func()) { return seq.seqPull() }
// All checks whether all elements in the iterator satisfy the provided condition.
// This function is useful when you want to determine if all elements in an iterator
// meet a specific criteria.
//
// Parameters:
// - fn func(V) bool: A function that returns a boolean indicating whether the element satisfies
// the condition.
//
// Returns:
// - bool: True if all elements in the iterator satisfy the condition, false otherwise.
//
// Example usage:
//
// slice := g.SliceOf(1, 2, 3, 4, 5, 6, 7, -1, -2)
// isPositive := func(num int) bool { return num > 0 }
// allPositive := slice.Iter().All(isPositive)
//
// The resulting allPositive will be true if all elements returned by the iterator are positive.
func (seq Seq[V]) All(fn func(v V) bool) bool {
all := true
seq(func(v V) bool {
if !fn(v) {
all = false
return false
}
return true
})
return all
}
// Any checks whether any element in the iterator satisfies the provided condition.
// This function is useful when you want to determine if at least one element in an iterator
// meets a specific criteria.
//
// Parameters:
// - fn func(V) bool: A function that returns a boolean indicating whether the element satisfies
// the condition.
//
// Returns:
// - bool: True if at least one element in the iterator satisfies the condition, false otherwise.
//
// Example usage:
//
// slice := g.Slice[int]{1, 3, 5, 7, 9}
// isEven := func(num int) bool { return num%2 == 0 }
// anyEven := slice.Iter().Any(isEven)
//
// The resulting anyEven will be true if at least one element returned by the iterator is even.
func (seq Seq[V]) Any(fn func(V) bool) bool {
found := false
seq(func(v V) bool {
if fn(v) {
found = true
return false
}
return true
})
return found
}
// Chain concatenates the current iterator with other iterators, returning a new iterator.
//
// The function creates a new iterator that combines the elements of the current iterator
// with elements from the provided iterators in the order they are given.
//
// Params:
//
// - seqs ([]Seq[V]): Other iterators to be concatenated with the current iterator.
//
// Returns:
//
// - Seq[V]: A new iterator containing elements from the current iterator and the provided iterators.
//
// Example usage:
//
// iter1 := g.Slice[int]{1, 2, 3}.Iter()
// iter2 := g.Slice[int]{4, 5, 6}.Iter()
// iter1.Chain(iter2).Collect().Slice().Print()
//
// Output: [1, 2, 3, 4, 5, 6]
//
// The resulting iterator will contain elements from both iterators in the specified order.
func (seq Seq[V]) Chain(seqs ...Seq[V]) Seq[V] {
return func(yield func(V) bool) {
proceed := true
seq(func(v V) bool {
if !yield(v) {
proceed = false
return false
}
return true
})
if !proceed {
return
}
for _, rest := range seqs {
rest(func(v V) bool {
if !yield(v) {
proceed = false
return false
}
return true
})
if !proceed {
return
}
}
}
}
// Chunks returns an iterator that yields chunks of elements of the specified size.
//
// The function creates a new iterator that yields chunks of elements from the original iterator,
// with each chunk containing elements of the specified size.
//
// Params:
//
// - n (Int): The size of each chunk.
//
// Returns:
//
// - SeqSlices[V]: An iterator yielding chunks of elements of the specified size.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5, 6}
// chunks := slice.Iter().Chunks(2).Collect().Slices()
//
// Output: [Slice[1, 2] Slice[3, 4] Slice[5, 6]]
//
// The resulting iterator will yield chunks of elements, each containing the specified number of elements.
func (seq Seq[V]) Chunks(n Int) SeqSlices[V] {
return func(yield func([]V) bool) {
size := n.Std()
if size <= 0 {
return
}
chunk := make([]V, 0, size)
seq(func(v V) bool {
chunk = append(chunk, v)
if len(chunk) == size {
if !yield(chunk) {
return false
}
chunk = make([]V, 0, size)
}
return true
})
if len(chunk) > 0 {
yield(chunk)
}
}
}
// Collect returns a collector over the grouped sequence; its Slices method
// materializes the groups. Collect itself is lazy and does not consume the
// sequence.
func (seqs SeqSlices[V]) Collect() collectorS[V] { return collectorS[V]{seqs} }
// collectorS materializes a grouped sequence; build one with
// SeqSlices.Collect.
type collectorS[V any] struct{ seq SeqSlices[V] }
// Slices consumes the sequence and returns the groups as a plain []Slice.
// (Returning a g.Slice here would re-close the instantiation cycle that keeps
// SeqSlices a separate type: Slice[Slice[V]].Iter().Chunks() would need
// SeqSlices[Slice[V]], and so on forever.)
func (c collectorS[V]) Slices() []Slice[V] {
collection := make([]Slice[V], 0)
c.seq(func(v []V) bool {
chunk := make(Slice[V], len(v))
copy(chunk, v)
collection = append(collection, chunk)
return true
})
return collection
}
// Map transforms each group (sub-slice) in the iterator using the given function.
//
// The function creates a new lazy iterator by applying the provided function to each
// group produced by the original iterator, preserving the streaming pipeline.
//
// Params:
//
// - fn (func(Slice[V]) Slice[U]): The function used to transform each group.
// The element type of the resulting groups may differ.
//
// Returns:
//
// - SeqSlices[U]: An iterator yielding the transformed groups.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4}
// doubled := slice.Iter().
// Chunks(2).
// Map(func(chunk g.Slice[int]) g.Slice[int] {
// return chunk.Iter().Map(func(v int) int { return v * 2 }).Collect().Slice()
// }).
// Collect().Slices()
// // Output: [Slice[2, 4] Slice[6, 8]]
func (seqs SeqSlices[V]) Map[U any](fn func(Slice[V]) Slice[U]) SeqSlices[U] {
return func(yield func([]U) bool) {
seqs(func(v []V) bool {
return yield(fn(Slice[V](v)))
})
}
}
// Filter returns a new iterator containing only the groups (sub-slices) that satisfy
// the provided function.
//
// The function applies the provided function to each group produced by the original
// iterator. If the function returns true for a group, that group is included in the
// resulting iterator.
//
// Params:
//
// - fn (func(Slice[V]) bool): The predicate applied to each group.
//
// Returns:
//
// - SeqSlices[V]: An iterator yielding the groups that satisfy the given condition.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5, 6}
// pairs := slice.Iter().
// Chunks(2).
// Filter(func(chunk g.Slice[int]) bool { return chunk.Len() == 2 }).
// Collect().Slices()
func (seqs SeqSlices[V]) Filter(fn func(Slice[V]) bool) SeqSlices[V] {
return func(yield func([]V) bool) {
seqs(func(v []V) bool {
if fn(Slice[V](v)) {
return yield(v)
}
return true
})
}
}
// ForEach iterates through all groups (sub-slices) and applies the given function to each.
//
// Params:
//
// - fn (func(Slice[V])): The function to apply to each group.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4}
// slice.Iter().Chunks(2).ForEach(func(chunk g.Slice[int]) {
// fmt.Println(chunk)
// })
func (seqs SeqSlices[V]) ForEach(fn func(s Slice[V])) {
seqs(func(v []V) bool {
fn(Slice[V](v))
return true
})
}
// Flatten flattens the iterator of groups (sub-slices) into a single Seq[V],
// yielding the elements of each group in order.
//
// Returns:
//
// - Seq[V]: A single iterator containing the elements from each group in sequence.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5, 6}
// flat := slice.Iter().Chunks(2).Flatten().Collect().Slice()
// // Output: Slice[1, 2, 3, 4, 5, 6]
func (seqs SeqSlices[V]) Flatten() Seq[V] {
return func(yield func(V) bool) {
seqs(func(v []V) bool {
for _, item := range v {
if !yield(item) {
return false
}
}
return true
})
}
}
// Count consumes the iterator, counting the number of iterations and returning it.
func (seq Seq[V]) Count() Int {
count := Int(0)
seq(func(V) bool { count++; return true })
return count
}
// CounterBy tallies elements by fn(element), returning a Seq2[K, Int] that yields
// each key with the number of elements that produced it: fn is applied to every
// element, and elements whose keys collide are merged into one bucket with their
// counts summed. Key order is first-seen; the source is consumed when the
// returned sequence is iterated. The key type must be comparable; for identity
// counting pass the identity function (func(v V) V { return v }).
//
// Example usage:
//
// words.Iter().CounterBy(func(w String) Int { return w.Len() })
// // yields 5:2, 4:1 — counts by word length, in first-seen order
func (seq Seq[V]) CounterBy[K comparable](fn func(V) K) Seq2[K, Int] {
return func(yield func(K, Int) bool) {
order := NewSlice[K]()
counts := NewMap[K, Int]()
seq(func(v V) bool {
k := fn(v)
if !counts.Contains(k) {
order.Push(k)
}
counts[k]++
return true
})
for _, k := range order {
if !yield(k, counts[k]) {
return
}
}
}
}
// ChunkBy groups CONSECUTIVE elements of the sequence into chunks based on a
// custom equality function. It is not an SQL-style
// GroupBy: elements are never reordered or bucketed by key, so equal elements
// that are not adjacent end up in different chunks.
//
// The provided function `fn` takes two consecutive elements `a` and `b` and returns `true`
// if they belong to the same chunk, or `false` if a new chunk should start.
// The function returns a `SeqSlices[V]`, where each `[]V` represents a run of consecutive
// elements that satisfy the provided equality condition.
//
// Notes:
// - Each chunk is returned as a copy of the elements, since `Seq` does not guarantee
// that elements share the same backing array.
//
// Parameters:
// - fn (func(a, b V) bool): Function that determines whether two consecutive elements belong to the same chunk.
//
// Returns:
// - SeqSlices[V]: An iterator yielding slices, each containing one chunk.
//
// Example usage:
//
// slice := g.SliceOf(1, 1, 2, 3, 2, 3, 4)
// chunks := slice.Iter().ChunkBy(func(a, b int) bool { return a <= b }).Collect().Slices()
// // Output: [Slice[1, 1, 2, 3] Slice[2, 3, 4]]
//
// The resulting iterator will yield runs of consecutive elements according to the provided function.
func (seq Seq[V]) ChunkBy(fn func(a, b V) bool) SeqSlices[V] {
return func(yield func([]V) bool) {
var (
group []V
prev V
)
first := true
cont := true
seq(func(v V) bool {
if first {
group = []V{v}
prev = v
first = false
return true
}
if fn(prev, v) {
group = append(group, v)
prev = v
return true
}
out := make([]V, len(group))
copy(out, group)
if !yield(out) {
cont = false
return false
}
group = []V{v}
prev = v
return true
})
if cont && len(group) > 0 {
out := make([]V, len(group))
copy(out, group)
_ = yield(out)
}
}
}
// Combinations generates all combinations of length 'size' from the sequence.
func (seq Seq[V]) Combinations(size Int) SeqSlices[V] {
return func(yield func([]V) bool) {
slice := seq.seqToSlice()
n := len(slice)
k := size.Std()
if k > n || k <= 0 {
return
}
indices := make([]int, k)
for i := range indices {
indices[i] = i
}
for {
combination := make([]V, k)
for i, idx := range indices {
combination[i] = slice[idx]
}
if !yield(combination) {
return
}
i := k - 1
for i >= 0 && indices[i] == n-k+i {
i--
}
if i < 0 {
break
}
indices[i]++
for j := i + 1; j < k; j++ {
indices[j] = indices[j-1] + 1
}
}
}
}
// Cycle returns an iterator that endlessly repeats the elements of the current sequence.
func (seq Seq[V]) Cycle() Seq[V] {
return func(yield func(V) bool) {
for {
yielded := false
keep := true
seq(func(v V) bool {
yielded = true
if !yield(v) {
keep = false
return false
}
return true
})
if !keep || !yielded {
return
}
}
}
}
// Enumerate adds an index to each element in the iterator.
//
// Returns:
//
// - Seq2[Int, V]: An iterator yielding (index, value) pairs, where the first
// element of the pair is the index and the second element is the original element from the
// iterator.
//
// Example usage:
//
// ps := g.SliceOf[g.String]("bbb", "ddd", "xxx", "aaa", "ccc").
// Iter().
// Enumerate().
// Collect().MapOrd[g.Int, g.String]()
//
// ps.Print()
//
// Output: MapOrd{0:bbb, 1:ddd, 2:xxx, 3:aaa, 4:ccc}
func (seq Seq[V]) Enumerate() Seq2[Int, V] {
return func(yield func(Int, V) bool) {
index := Int(0)
seq(func(v V) bool {
result := yield(index, v)
index++
return result
})
}
}
// Dedup creates a new iterator that removes consecutive duplicate elements from the original iterator,
// leaving only one occurrence of each unique element. If the iterator is sorted, all elements will be unique.
//
// Parameters:
// - None
//
// Returns:
// - Seq[V]: A new iterator with consecutive duplicates removed.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 2, 3, 4, 4, 4, 5}
// iter := slice.Iter().Dedup()
// result := iter.Collect().Slice()
// result.Print()
//
// Output: [1 2 3 4 5]
//
// The resulting iterator will contain only unique elements, removing consecutive duplicates.
func (seq Seq[V]) Dedup() Seq[V] {
if isValueComparable[V]() {
return Seq[V](seq.seqDedupBy(func(a, b V) bool {
return any(a) == any(b)
}))
}
return Seq[V](seq.seqDedupBy(func(a, b V) bool {
return reflect.DeepEqual(a, b)
}))
}
// Filter returns a new iterator containing only the elements that satisfy the provided function.
//
// The function applies the provided function to each element of the iterator.
// If the function returns true for an element, that element is included in the resulting iterator.
//
// Parameters:
//
// - fn (func(V) bool): The function to be applied to each element of the iterator
// to determine if it should be included in the result.
//
// Returns:
//
// - Seq[V]: A new iterator containing the elements that satisfy the given condition.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// even := slice.Iter().
// Filter(
// func(val int) bool {
// return val%2 == 0
// }).
// Collect().Slice()
// even.Print()
//
// Output: [2 4].
//
// The resulting iterator will contain only the elements that satisfy the provided function.
func (seq Seq[V]) Filter(fn func(V) bool) Seq[V] {
return func(yield func(V) bool) {
seq(func(v V) bool {
if fn(v) {
return yield(v)
}
return true
})
}
}
// Exclude returns a new iterator excluding elements that satisfy the provided function.
//
// The function applies the provided function to each element of the iterator.
// If the function returns true for an element, that element is excluded from the resulting iterator.
//
// Parameters:
//
// - fn (func(V) bool): The function to be applied to each element of the iterator
// to determine if it should be excluded from the result.
//
// Returns:
//
// - Seq[V]: A new iterator containing the elements that do not satisfy the given condition.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// notEven := slice.Iter().
// Exclude(
// func(val int) bool {
// return val%2 == 0
// }).
// Collect().Slice()
// notEven.Print()
//
// Output: [1, 3, 5]
//
// The resulting iterator will contain only the elements that do not satisfy the provided function.
func (seq Seq[V]) Exclude(fn func(V) bool) Seq[V] {
return func(yield func(V) bool) {
seq(func(v V) bool {
if !fn(v) {
return yield(v)
}
return true
})
}
}
// Fold accumulates values in the iterator using a function.
//
// The function iterates through the elements of the iterator, accumulating values
// using the provided function and an initial value.
//
// Params:
//
// - init (A): The initial value for accumulation. The accumulator type may differ
// from the element type.
// - fn (func(A, V) A): The function that accumulates values; it takes the accumulator
// and an element and returns the new accumulator.
//
// Returns:
//
// - A: The accumulated value after applying the function to all elements.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// sum := slice.Iter().
// Fold(0,
// func(acc, val int) int {
// return acc + val
// })
// fmt.Println(sum)
//
// Output: 15.
//
// The resulting value will be the accumulation of elements based on the provided function.
func (seq Seq[V]) Fold[A any](init A, fn func(acc A, val V) A) A {
seq(func(v V) bool { init = fn(init, v); return true })
return init
}
// SumBy maps each element to a numeric value via fn and returns the sum of those values.
// An empty sequence yields the zero value of S. The result type S is chosen by fn,
// independent of the element type V.
//
// Params:
// - fn (func(V) S): Projects an element to the numeric value to be summed.
//
// Returns:
// - S: The sum of the projected values.
//
// Example usage:
//
// words := g.SliceOf[g.String]("a", "bb", "ccc")
// total := words.Iter().SumBy(func(s g.String) g.Int { return s.Len() })
// fmt.Println(total) // 6
func (seq Seq[V]) SumBy[S constraints.Number](fn func(V) S) S {
var zero S
return seq.Fold(zero, func(acc S, v V) S { return acc + fn(v) })
}
// ProductBy maps each element to a numeric value via fn and returns their product.
// An empty sequence yields the multiplicative identity, one.
func (seq Seq[V]) ProductBy[S constraints.Number](fn func(V) S) S {
return seq.Fold(S(1), func(acc S, v V) S { return acc * fn(v) })
}
// FindMap applies fn to each element and returns the first Some result, or None
// if fn returns None for every element.
func (seq Seq[V]) FindMap[U any](fn func(V) Option[U]) Option[U] {
var result Option[U]
seq(func(v V) bool {
if o := fn(v); o.IsSome() {
result = o
return false
}
return true
})
return result
}
// Reduce aggregates elements of the sequence using the provided function.
// The first element of the sequence is used as the initial accumulator value.
// If the sequence is empty, it returns None[V].
//
// Params:
// - fn (func(V, V) V): Function that combines two values into one.
//
// Returns:
// - Option[V]: The accumulated value wrapped in Some, or None if the sequence is empty.
//
// Example:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// product := slice.Iter().Reduce(func(a, b int) int { return a * b })
// if product.IsSome() {
// fmt.Println(product.Some()) // 120
// } else {
// fmt.Println("empty")
// }
func (seq Seq[V]) Reduce(fn func(a, b V) V) Option[V] {
var result V
first := true
seq(func(v V) bool {
if first {
result = v
first = false
} else {
result = fn(result, v)
}
return true
})
return OptionOf(result, !first)
}
// ForEach iterates through all elements and applies the given function to each.
//
// The function applies the provided function to each element of the iterator.
//
// Params:
//
// - fn (func(V)): The function to apply to each element.
//
// Example usage:
//
// iter := g.Slice[int]{1, 2, 3, 4, 5}.Iter()
// iter.ForEach(func(val int) {
// fmt.Println(val) // Replace this with the function logic you need.
// })
//
// The provided function will be applied to each element in the iterator.
func (seq Seq[V]) ForEach(fn func(v V)) {
seq(func(v V) bool { fn(v); return true })
}
// Flatten recursively flattens nested slices and arrays inside each element,
// yielding the leaf elements in order.
//
// The function relies on reflection: slices and arrays at any depth are
// descended into, and only leaf values assignable to V are yielded.
//
// Returns:
//
// - Seq[V]: A single iterator containing the flattened leaf elements.
//
// Example usage:
//
// nestedSlice := g.Slice[any]{
// 1,
// g.SliceOf(2, 3),
// "abc",
// g.SliceOf("def", "ghi"),
// g.SliceOf(4.5, 6.7),
// }
//
// nestedSlice.Iter().Flatten().Collect().Slice().Print()
//
// Output: Slice[1, 2, 3, abc, def, ghi, 4.5, 6.7]
//
// The resulting iterator will contain elements from each iterator in sequence.
func (seq Seq[V]) Flatten() Seq[V] {
return func(yield func(V) bool) {
seq(func(item V) bool {
return flattenValue(item, yield)
})
}
}
// Inspect creates a new iterator that wraps around the current iterator
// and allows inspecting each element as it passes through.
func (seq Seq[V]) Inspect(fn func(v V)) Seq[V] {
return func(yield func(V) bool) { seq(func(v V) bool { fn(v); return yield(v) }) }
}
// Intersperse inserts the provided separator between elements of the iterator.
//
// The function creates a new iterator that inserts the given separator between each
// consecutive pair of elements in the original iterator.
//
// Params:
//
// - sep (V): The separator to intersperse between elements.
//
// Returns:
//
// - Seq[V]: An iterator containing elements with the separator interspersed.
//
// Example usage:
//
// g.Slice[string]{"Hello", "World", "!"}.
// Iter().
// Intersperse(" ").
// Collect().
// Slice().
// Join().
// Print()
//
// Output: "Hello World !".
//
// The resulting iterator will contain elements with the separator interspersed.
func (seq Seq[V]) Intersperse(sep V) Seq[V] {
return func(yield func(V) bool) {
first := true
seq(func(v V) bool {
if !first {
if !yield(sep) {
return false
}
}
first = false
return yield(v)
})
}
}
// Map transforms each element in the iterator using the given function.
//
// The function creates a new iterator by applying the provided function to each element
// of the original iterator.
//
// Params:
//
// - transform (func(V) U): The function used to transform elements. The result
// type may differ from the element type.
//
// Returns:
//
// - Seq[U]: A iterator containing elements transformed by the provided function.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3}
// strs := slice.
// Iter().
// Map(
// func(val int) g.String {
// return g.Int(val * 2).String()
// }).
// Collect().Slice()
// strs.Print()
//
// Output: Slice[2, 4, 6].
//
// The resulting iterator will contain elements transformed by the provided function.
func (seq Seq[V]) Map[U any](transform func(V) U) Seq[U] {
return func(yield func(U) bool) { seq(func(v V) bool { return yield(transform(v)) }) }
}
// FlatMap applies a function to each element that returns an iterator, then flattens the results.
//
// The function transforms each element into a sequence and then concatenates all sequences
// into a single flat sequence.
//
// Params:
//
// - fn (func(V) Seq[U]): The function that transforms each element into a sequence.
// The element type of the resulting sequences may differ.
//
// Returns:
//
// - Seq[U]: A flattened sequence containing all elements from the transformed sequences.
//
// Example usage:
//
// words := g.Slice[string]{"hello world", "foo bar"}.Iter()
// chars := words.FlatMap(func(s string) Seq[string] {
// return g.String(s).Split("")
// })
// // chars will yield: "h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "f", "o", "o", " ", "b", "a", "r"
//
// numbers := g.Slice[int]{1, 2, 3}.Iter()
// expanded := numbers.FlatMap(func(n int) Seq[int] {
// return g.Slice[int]{n, n*10, n*100}.Iter()
// })
// // expanded will yield: 1, 10, 100, 2, 20, 200, 3, 30, 300
func (seq Seq[V]) FlatMap[U any](fn func(V) Seq[U]) Seq[U] {
return func(yield func(U) bool) {
seq(func(v V) bool {
keep := true
fn(v)(func(u U) bool {
if !yield(u) {
keep = false
return false
}
return true
})
return keep
})
}
}
// FilterMap applies a function to each element and filters out None results.
//
// The function transforms and filters in a single pass. Elements where the function
// returns None are filtered out, and elements where it returns Some are unwrapped
// and included in the result.
//
// Params:
//
// - fn (func(V) Option[U]): The function that transforms and filters elements.
// Returns Some(value) to include the transformed value, or None to filter it out.
// The result type may differ from the element type.
//
// Returns:
//
// - Seq[U]: A sequence containing only the successfully transformed elements.
//
// Example usage:
//
// strings := g.Slice[string]{"1", "2", "abc", "3", "xyz"}.Iter()
// numbers := strings.FilterMap(func(s string) Option[int] {
// if n, err := strconv.Atoi(s); err == nil {
// return Some(n)
// }
// return None[int]()
// })
// // numbers will yield: 1, 2, 3
//
// values := g.Slice[int]{1, -2, 3, -4, 5}.Iter()
// positiveDoubled := values.FilterMap(func(n int) Option[int] {
// if n > 0 {
// return Some(n * 2)
// }
// return None[int]()
// })
// // positiveDoubled will yield: 2, 6, 10
func (seq Seq[V]) FilterMap[U any](fn func(V) Option[U]) Seq[U] {
return func(yield func(U) bool) {
seq(func(v V) bool {
if u, ok := fn(v).Option(); ok {
return yield(u)
}
return true
})
}
}
// TryMap applies a fallible transform to each element and enters the Result
// pipeline, producing a SeqResult[U]. It is the bridge from a plain sequence
// into SeqResult: map each element to a Result[U] and continue with the
// SeqResult terminals (TryCollect, SumBy, ...), which choose the Err policy.
//
// TryMap itself is lazy and consumer-driven: it yields fn(v) for each element
// and leaves the Err policy to the terminal — TryCollect / Fold / Reduce /
// SumBy / All / Any / First short-circuit on the first Err, while Collect and
// Count traverse every element.
//
// Example usage:
//
// // "abc" fails to parse -> the whole batch short-circuits
// res := g.SliceOf[g.String]("1", "2", "3").
// Iter().
// TryMap(g.String.TryInt).
// TryCollect() // Ok(Slice[1, 2, 3])
//
// sum := g.SliceOf[g.String]("1", "2", "3").
// Iter().
// TryMap(g.String.TryInt).
// SumBy(f.Id) // Ok(6)
func (seq Seq[V]) TryMap[U any](fn func(V) Result[U]) SeqResult[U] {
return func(yield func(Result[U]) bool) {
seq(func(v V) bool {
return yield(fn(v))
})
}
}
// Partition divides the elements of the iterator into two separate slices based on a given predicate function.
//
// The function takes a predicate function 'fn', which should return true or false for each element in the iterator.
// Elements for which 'fn' returns true are collected into the left slice, while those for which 'fn' returns false
// are collected into the right slice.
//
// Params:
//
// - fn (func(V) bool): The predicate function used to determine the placement of elements.
//
// Returns:
//
// - (Slice[V], Slice[V]): Two slices representing elements that satisfy and don't satisfy the predicate, respectively.
//
// Example usage:
//
// evens, odds := g.Slice[int]{1, 2, 3, 4, 5}.
// Iter().
// Partition(
// func(v int) bool {
// return v%2 == 0
// })
//
// fmt.Println("Even numbers:", evens) // Output: Even numbers: Slice[2, 4]
// fmt.Println("Odd numbers:", odds) // Output: Odd numbers: Slice[1, 3, 5]
//
// The resulting two slices will contain elements separated based on whether they satisfy the predicate or not.
func (seq Seq[V]) Partition(fn func(v V) bool) (Slice[V], Slice[V]) {
var left, right Slice[V]
seq(func(v V) bool {
if fn(v) {
left = append(left, v)
} else {
right = append(right, v)
}
return true
})
return left, right
}
// Permutations generates all permutations of the elements.
//
// The function uses a recursive approach to generate all the permutations of the elements.
// If the iterator is empty, the resulting iterator is empty as well; a single-element
// iterator yields exactly one permutation.
//
// Returns:
//
// - SeqSlices[V]: An iterator yielding slices, each containing one permutation of the
// elements in the iterator.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3}
// perms := slice.Iter().Permutations().Collect().Slices()
// for _, perm := range perms {
// fmt.Println(perm)
// }
//
// Output:
//
// Slice[1, 2, 3]
// Slice[2, 1, 3]
// Slice[3, 1, 2]
// Slice[1, 3, 2]
// Slice[2, 3, 1]
// Slice[3, 2, 1]
//
// The resulting iterator will yield slices representing all possible permutations
// of the elements in the original iterator.
func (seq Seq[V]) Permutations() SeqSlices[V] {
return func(yield func([]V) bool) {
slice := seq.seqToSlice()
n := len(slice)
if n == 0 {
return
}
var generate func(int) bool
generate = func(k int) bool {
if k == 1 {
perm := make([]V, n)
copy(perm, slice)
return yield(perm)
}
for i := range k {
if !generate(k - 1) {
return false
}
if k%2 == 0 {
slice[i], slice[k-1] = slice[k-1], slice[i]
} else {
slice[0], slice[k-1] = slice[k-1], slice[0]
}
}
return true
}
generate(n)
}
}
// Range iterates through elements until the given function returns false.
//
// The function iterates through the elements of the iterator and applies the provided function
// to each element. It stops iteration when the function returns false for an element.
//
// Params:
//
// - fn (func(V) bool): The function that evaluates elements for continuation of iteration.
//
// Example usage:
//
// iter := g.Slice[int]{1, 2, 3, 4, 5}.Iter()
// iter.Range(func(val int) bool {
// fmt.Println(val) // Replace this with the function logic you need.
// return val < 5 // Replace this with the condition for continuing iteration.
// })
//
// The iteration will stop when the provided function returns false for an element.
func (seq Seq[V]) Range(fn func(v V) bool) {
seq(fn)
}
// Skip returns a new iterator skipping the first n elements.
//
// The function creates a new iterator that skips the first n elements of the current iterator
// and returns an iterator starting from the (n+1)th element.
//
// Params:
//
// - n (Int): The number of elements to skip from the beginning of the iterator.
// Negative values are treated as zero.
//
// Returns:
//
// - Seq[V]: An iterator that starts after skipping the first n elements.
//
// Example usage:
//
// iter := g.Slice[int]{1, 2, 3, 4, 5, 6}.Iter()
// iter.Skip(3).Collect().Slice().Print()
//
// Output: [4, 5, 6]
//
// The resulting iterator will start after skipping the specified number of elements.
func (seq Seq[V]) Skip(n Int) Seq[V] {
return func(yield func(V) bool) {
if n <= 0 {
seq(yield)
return
}
count := Int(0)
seq(func(v V) bool {
if count < n {
count++
return true
}
return yield(v)
})
}
}
// StepBy creates a new iterator that iterates over every N-th element of the original iterator.
// This function is useful when you want to skip a specific number of elements between each iteration.
//
// Parameters:
// - n Int: The step size, indicating how many elements to skip between each iteration.
//
// Returns:
// - Seq[V]: A new iterator that produces elements from the original iterator with a step size of N.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// iter := slice.Iter().StepBy(3)
// result := iter.Collect().Slice()
// result.Print()
//
// Output: Slice[1, 4, 7, 10]
//
// The resulting iterator will produce elements from the original iterator with a step size of N.
func (seq Seq[V]) StepBy(n Int) Seq[V] {
return func(yield func(V) bool) {
if n <= 0 {
return
}
index := Int(0)
seq(func(v V) bool {
if index%n == 0 {
if !yield(v) {
return false
}
}
index++
return true
})
}
}
// SortBy applies a custom sorting function to the elements in the iterator
// and returns a new iterator containing the sorted elements.
//
// The sorting function 'fn' should take two arguments, 'a' and 'b' of type V,
// and return a cmp.Ordering: cmp.Less orders 'a' before 'b', cmp.More after it.
//
// Example:
//
// g.SliceOf[g.String]("a", "c", "b").
// Iter().
// SortBy(func(a, b g.String) cmp.Ordering { return b.Cmp(a) }).
// Collect().
// Slice().
// Print()
//
// Output: Slice[c, b, a]
//
// The returned iterator is of type Seq[V], which implements the iterator
// interface for further iteration over the sorted elements.
func (seq Seq[V]) SortBy(fn func(a, b V) cmp.Ordering) Seq[V] {
slice := seq.seqToSlice()
slices.SortFunc(slice, func(a, b V) int { return int(fn(a, b)) })
return seqFromSlice(slice)
}
// Take returns a new iterator with the first n elements.
// The function creates a new iterator containing the first n elements from the original iterator.
func (seq Seq[V]) Take(n Int) Seq[V] {
return func(yield func(V) bool) {
if n <= 0 {
return
}
count := Int(0)
seq(func(v V) bool {
if count >= n {
return false
}
count++
return yield(v)
})
}
}
// First returns the first element from the sequence.
func (seq Seq[V]) First() Option[V] {
var result V
found := false
seq(func(v V) bool {
result = v
found = true
return false
})
return OptionOf(result, found)
}
// Last returns the last element from the sequence.
func (seq Seq[V]) Last() Option[V] {
var result V
found := false
seq(func(v V) bool {
result = v
found = true
return true
})
return OptionOf(result, found)
}
// Nth returns the nth element (0-indexed) in the sequence.
func (seq Seq[V]) Nth(n Int) Option[V] {
var result V
found := false
index := Int(0)
seq(func(v V) bool {
if index == n {
result = v
found = true
return false
}
index++
return true
})
return OptionOf(result, found)
}
// Chan converts the iterator into a channel, optionally with context(s).
//
// The function converts the elements of the iterator into a channel for streaming purposes.
// Optionally, it accepts context(s) to handle cancellation or timeout scenarios.
//
// Params:
//
// - ctxs (context.Context): Optional context(s) to control the channel behavior (e.g., cancellation).
//
// Returns:
//
// - chan V: A channel containing the elements from the iterator.
//
// Example usage:
//
// iter := g.Slice[int]{1, 2, 3}.Iter()
// ctx, cancel := context.WithCancel(context.Background())
// defer cancel() // Ensure cancellation to avoid goroutine leaks.
// ch := iter.Chan(ctx)
// for val := range ch {
// fmt.Println(val)
// }
//
// The resulting channel allows streaming elements from the iterator with optional context handling.
func (seq Seq[V]) Chan(ctxs ...context.Context) chan V {
ctx := context.Background()
if len(ctxs) > 0 {
ctx = ctxs[0]
}
ch := make(chan V)
go func() {
defer close(ch)
if err := ctx.Err(); err != nil {
return
}
seq(func(v V) bool {
select {
case <-ctx.Done():
return false
case ch <- v:
return true
}
})
}()
return ch
}
// Unique returns an iterator with only unique elements.
//
// The function returns an iterator containing only the unique elements from the original iterator.
//
// Returns:
//
// - Seq[V]: An iterator containing unique elements from the original iterator.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 2, 4, 5, 3}
// unique := slice.Iter().Unique().Collect().Slice()
// unique.Print()
//
// Output: [1, 2, 3, 4, 5].
//
// The resulting iterator will contain only unique elements from the original iterator.
func (seq Seq[V]) Unique() Seq[V] {
return func(yield func(V) bool) {
seen := make(map[any]struct{})
seq(func(v V) bool {
k := any(v)
if _, exists := seen[k]; !exists {
seen[k] = struct{}{}
return yield(v)
}
return true
})
}
}
// Zip combines elements from the current sequence and another sequence into pairs.
// The element types of the two sequences may differ. Iteration stops when either
// sequence is exhausted.
func (seq Seq[V]) Zip[U any](two Seq[U]) Seq2[V, U] {
return func(yield func(V, U) bool) {
next, stop := seq.seqPull()
defer stop()
nextU, stopU := two.seqPull()
defer stopU()
for {
v, ok := next()
if !ok {
return
}
u, ok := nextU()
if !ok {
return
}
if !yield(v, u) {
return
}
}
}
}
// Scan accumulates values of the iterator using a function, yielding all intermediate states.
//
// The function takes an initial accumulator value and a function that combines the accumulator
// with each element. It yields the initial value followed by each accumulated state.
//
// Params:
//
// - init (A): The initial accumulator value. The accumulator type may differ from the element type.
// - fn (func(acc A, val V) A): The function that combines the accumulator with each element.
//
// Returns:
//
// - Seq[A]: A sequence of all intermediate accumulator states.
//
// Example usage:
//
// numbers := g.Slice[int]{1, 2, 3, 4}.Iter()
// sums := numbers.Scan(0, func(acc, val int) int {
// return acc + val
// })
// // sums will yield: 0, 1, 3, 6, 10
//
// words := g.Slice[string]{"a", "b", "c"}.Iter()
// concatenated := words.Scan("", func(acc, val string) string {
// return acc + val
// })
// // concatenated will yield: "", "a", "ab", "abc"
func (seq Seq[V]) Scan[A any](init A, fn func(acc A, val V) A) Seq[A] {
return func(yield func(A) bool) {
if !yield(init) {
return
}
acc := init
seq(func(v V) bool {
acc = fn(acc, v)
return yield(acc)
})
}
}
// Find searches for an element in the iterator that satisfies the provided function.
//
// The function iterates through the elements of the iterator and returns the first element
// for which the provided function returns true.
//
// Params:
//
// - fn (func(V) bool): The function used to test elements for a condition.
//
// Returns:
//
// - Option[V]: An Option containing the first element that satisfies the condition; None if not found.
//
// Example usage:
//
// iter := g.Slice[int]{1, 2, 3, 4, 5}.Iter()
//
// found := iter.Find(func(i int) bool {
// return i == 2
// })
//
// if found.IsSome() {
// fmt.Println("Found:", found.Some())
// } else {
// fmt.Println("Not found.")
// }
//
// The resulting Option may contain the first element that satisfies the condition, or None if not found.
func (seq Seq[V]) Find(fn func(v V) bool) Option[V] {
var result V
found := false
seq(func(v V) bool {
if fn(v) {
result = v
found = true
return false
}
return true
})
return OptionOf(result, found)
}
// Windows returns an iterator that yields sliding windows of elements of the specified size.
//
// The function creates a new iterator that yields windows of elements from the original iterator,
// where each window is a slice containing elements of the specified size and moves one element at a time.
//
// Params:
//
// - n (Int): The size of each window.
//
// Returns:
//
// - SeqSlices[V]: An iterator yielding sliding windows of elements of the specified size.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5, 6}
// windows := slice.Iter().Windows(3).Collect().Slices()
//
// Output: [Slice[1, 2, 3] Slice[2, 3, 4] Slice[3, 4, 5] Slice[4, 5, 6]]
//
// The resulting iterator will yield sliding windows of elements, each containing the specified number of elements.
func (seq Seq[V]) Windows(n Int) SeqSlices[V] {
return func(yield func([]V) bool) {
size := n.Std()
if size <= 0 {
return
}
window := make([]V, 0, size)
seq(func(v V) bool {
window = append(window, v)
if len(window) == size {
windowCopy := make([]V, size)
copy(windowCopy, window)
if !yield(windowCopy) {
return false
}
window = window[1:]
}
return true
})
}
}
// Context allows the iteration to be controlled with a context.Context.
func (seq Seq[V]) Context(ctx context.Context) Seq[V] {
return func(yield func(V) bool) {
if err := ctx.Err(); err != nil {
return
}
seq(func(v V) bool {
select {
case <-ctx.Done():
return false
default:
return yield(v)
}
})
}
}
// MaxBy returns the maximum element in the sequence using the provided comparison function.
func (seq Seq[V]) MaxBy(fn func(V, V) cmp.Ordering) Option[V] {
var max V
found := false
seq(func(v V) bool {
if !found || fn(max, v) == cmp.Less {
max = v
found = true
}
return true
})
return OptionOf(max, found)
}
// MinBy returns the minimum element in the sequence using the provided comparison function.
func (seq Seq[V]) MinBy(fn func(V, V) cmp.Ordering) Option[V] {
var min V
found := false
seq(func(v V) bool {
if !found || fn(v, min) == cmp.Less {
min = v
found = true
}
return true
})
return OptionOf(min, found)
}
// Next extracts the next element from the iterator and advances it.
//
// This method consumes the next element from the iterator and returns it wrapped in an Option.
// The iterator itself is modified to point to the remaining elements.
// This is similar to calling Pull() but more convenient for single-element extraction.
//
// Returns:
// - Option[V]: Some(value) if an element exists, None if the iterator is exhausted.
func (seq *Seq[V]) Next() Option[V] {
if value, remaining, ok := (*seq).seqNext(); ok {
*seq = Seq[V](remaining)
return Some(value)
}
return None[V]()
}
// FromChan converts a channel into an iterator.
//
// This function takes a channel as input and converts its elements into an iterator,
// allowing seamless integration of channels into iterator-based processing pipelines.
// It continuously reads from the channel until it's closed,
// yielding each element to the provided yield function.
//
// Parameters:
// - ch (<-chan V): The input channel to convert into an iterator.
//
// Returns:
// - Seq[V]: An iterator that yields elements from the channel.
//
// Example usage:
//
// ch := make(chan int)
// go func() {
// defer close(ch)
// for i := 1; i <= 5; i++ {
// ch <- i
// }
// }()
//
// // Convert the channel into an iterator and apply filtering and mapping operations.
// g.FromChan(ch).
// Filter(func(i int) bool { return i%2 == 0 }). // Filter even numbers.
// Map(func(i int) int { return i * 2 }). // Double each element.
// Collect().Slice(). // Collect the results into a slice.
// Print() // Print the collected results.
//
// Output: Slice[4, 8]
//
// The resulting iterator will yield elements from the provided channel, filtering out odd numbers,
// doubling each even number, and finally collecting the results into a slice.
func FromChan[V any](ch <-chan V) Seq[V] {
return Seq[V](seqFromChan(ch))
}
// TakeWhile yields elements while the predicate returns true, stopping at the first false.
func (seq Seq[V]) TakeWhile(fn func(V) bool) Seq[V] {
return func(yield func(V) bool) {
seq(func(v V) bool {
if !fn(v) {
return false
}
return yield(v)
})
}
}
// SkipWhile skips elements while the predicate returns true, then yields the rest.
func (seq Seq[V]) SkipWhile(fn func(V) bool) Seq[V] {
return func(yield func(V) bool) {
skipping := true
seq(func(v V) bool {
if skipping && fn(v) {
return true
}
skipping = false
return yield(v)
})
}
}
// ── iterator core (value sequences), ported from github.com/enetx/iter (MIT) ──
// seqDedupBy removes consecutive elements where the provided function returns the same value.
func (seq Seq[T]) seqDedupBy(eq func(a, b T) bool) Seq[T] {
return func(yield func(T) bool) {
var prev T
first := true
seq(func(v T) bool {
if first || !eq(prev, v) {
prev = v
first = false
return yield(v)
}
return true
})
}
}
// seqFromChan creates a sequence from a channel.
// The sequence will stop when the channel is closed.
func seqFromChan[T any](ch <-chan T) Seq[T] {
return func(yield func(T) bool) {
for v := range ch {
if !yield(v) {
return
}
}
}
}
// seqFromSlice creates a sequence that iterates over the given slice in forward order.
func seqFromSlice[T any](sl []T) Seq[T] {
return func(yield func(T) bool) {
for _, v := range sl {
if !yield(v) {
return
}
}
}
}
// seqNext extracts the first element from the sequence and returns the remaining sequence.
// Returns (value, remainingSeq, true) if an element exists, or (zero, nil, false) if empty.
func (seq Seq[T]) seqNext() (T, Seq[T], bool) {
next, stop := seq.seqPull()
first, ok := next()
if !ok {
stop()
var zero T
return zero, nil, false
}
// The remaining sequence continues from the same pull iterator, so the source
// is walked exactly once. This makes Next O(1) per element and correct for
// non-deterministic sources (e.g. map-backed sets, channels), at the cost of
// the remaining sequence being single-use.
remaining := func(yield func(T) bool) {
defer stop()
for {
v, ok := next()
if !ok {
return
}
if !yield(v) {
return
}
}
}
return first, remaining, true
}
// seqPull converts a push-style iterator (Seq) to a pull-style iterator.
// Returns a next function that yields the next value and a boolean indicating if valid,
// and a stop function that should be called to release resources.
func (seq Seq[T]) seqPull() (next func() (T, bool), stop func()) {
return iter.Pull(iter.Seq[T](seq))
}
// seqToSlice collects all elements from the sequence into a slice.
func (seq Seq[T]) seqToSlice() []T {
out := make([]T, 0)
seq(func(v T) bool {
out = append(out, v)
return true
})
return out
}
// ── shared helpers (formerly iter_shared.go) ──
// isValueComparable reports whether values of type V can be compared with ==
// (V is a comparable type and not the bare interface any). It is the shared
// guard used by Dedup/Unique across the container and iterator types.
func isValueComparable[V any]() bool {
return f.IsComparable[V]() && reflect.TypeFor[V]().Kind() != reflect.Interface
}
// flattenValue recursively descends slices and arrays within item, emitting each
// leaf element assignable to V. It stops and returns false as soon as emit
// returns false. It backs the Flatten method across the Seq types.
func flattenValue[V any](item any, emit func(V) bool) bool {
rv := reflect.ValueOf(item)
switch rv.Kind() {
case reflect.Slice, reflect.Array:
for i := range rv.Len() {
if !flattenValue(rv.Index(i).Interface(), emit) {
return false
}
}
default:
if v, ok := item.(V); ok {
if !emit(v) {
return false
}
}
}
return true
}