feat: new iter package (#672)

* lint: pin golangci-lint version

* lint: fix issues triggered by go1.23 upgrade

* feat: new iter package

* lint: fix linter issues

* fix: restore go1.18

* fix: rename package to "it"

* feat: assign multiple sequences of maps

* fix: panic in DropRight if n = 0

* docs: fix incorrect non-iter helper references

* feat: implement Invert helper

* feat: helpers for creating and checking empty sequences

* feat: implement Reverse helper

* feat: implement ReduceRight helper

* feat: implement Shuffle helper

* feat: implement Sample* helpers

* refactor: rename helpers with Seq convention

* feat: implement SeqToChannel2 helper

* feat: implement HasPrefix/HasSuffix helpers

* chore: port recent changes

* perf: only iterate collection once in Every

* refactor: reduce dupe code by reusing helpers internally

* perf: reuse internal Mode slice

* feat: implement Length helper

* chore: duplicate unit tests for *I helpers

* fix: omit duplicates in second Intersect list

* feat: intersect more than 2 sequences

* feat: implement Drain helper

* feat: implement Seq/Seq2 conversion helpers

* refactor: rename *Right* to *Last*

* chore: minor cleanup

* refactor: consistent predicate/transform parameter names

* perf: abort Slice/Subset once upper bound reached

* refactor: rename IsSortedByKey to IsSortedBy

* refactor: reuse more helpers internally

* feat: implement Cut* helpers

* feat: implement Trim* helpers

* perf: reduce allocations

* docs: describe iteration and allocation expectations

* Update .github/workflows/lint.yml

---------

Co-authored-by: Samuel Berthe <dev@samuel-berthe.fr>
This commit is contained in:
Nathan Baulch
2025-10-03 03:23:16 +10:00
committed by GitHub
parent 7feda10eb3
commit 43cef1f439
31 changed files with 8613 additions and 12 deletions
+50
View File
@@ -0,0 +1,50 @@
//go:build go1.23
package it
import (
"iter"
"github.com/samber/lo"
)
// SeqToChannel returns a read-only channel of collection elements.
func SeqToChannel[T any](bufferSize int, collection iter.Seq[T]) <-chan T {
ch := make(chan T, bufferSize)
go func() {
for item := range collection {
ch <- item
}
close(ch)
}()
return ch
}
// SeqToChannel2 returns a read-only channel of collection elements.
func SeqToChannel2[K, V any](bufferSize int, collection iter.Seq2[K, V]) <-chan lo.Tuple2[K, V] {
ch := make(chan lo.Tuple2[K, V], bufferSize)
go func() {
for k, v := range collection {
ch <- lo.Tuple2[K, V]{A: k, B: v}
}
close(ch)
}()
return ch
}
// ChannelToSeq returns a sequence built from channels items. Blocks until channel closes.
func ChannelToSeq[T any](ch <-chan T) iter.Seq[T] {
return func(yield func(T) bool) {
for item := range ch {
if !yield(item) {
return
}
}
}
}
+60
View File
@@ -0,0 +1,60 @@
//go:build go1.23
package it
import (
"maps"
"slices"
"testing"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
)
func TestSeqToChannel(t *testing.T) {
t.Parallel()
is := assert.New(t)
ch := SeqToChannel(2, values(1, 2, 3))
r1, ok1 := <-ch
r2, ok2 := <-ch
r3, ok3 := <-ch
is.True(ok1)
is.Equal(1, r1)
is.True(ok2)
is.Equal(2, r2)
is.True(ok3)
is.Equal(3, r3)
_, ok4 := <-ch
is.False(ok4)
}
func TestSeqToChannel2(t *testing.T) {
t.Parallel()
is := assert.New(t)
ch := SeqToChannel2(2, maps.All(map[string]int{"a": 1, "b": 2, "c": 3}))
r1, ok1 := <-ch
r2, ok2 := <-ch
r3, ok3 := <-ch
is.True(ok1)
is.True(ok2)
is.True(ok3)
is.ElementsMatch([]lo.Tuple2[string, int]{{A: "a", B: 1}, {A: "b", B: 2}, {A: "c", B: 3}}, []lo.Tuple2[string, int]{r1, r2, r3})
_, ok4 := <-ch
is.False(ok4)
}
func TestChannelToSeq(t *testing.T) {
t.Parallel()
is := assert.New(t)
ch := SeqToChannel(2, values(1, 2, 3))
items := ChannelToSeq(ch)
is.Equal([]int{1, 2, 3}, slices.Collect(items))
}
+481
View File
@@ -0,0 +1,481 @@
//go:build go1.23
package it
import (
"fmt"
"iter"
"slices"
"time"
"github.com/samber/lo"
"github.com/samber/lo/internal/constraints"
"github.com/samber/lo/internal/rand"
)
// IndexOf returns the index at which the first occurrence of a value is found in a sequence or -1
// if the value cannot be found.
// Will iterate through the entire sequence if element is not found.
func IndexOf[T comparable](collection iter.Seq[T], element T) int {
var i int
for item := range collection {
if item == element {
return i
}
i++
}
return -1
}
// LastIndexOf returns the index at which the last occurrence of a value is found in a sequence or -1
// if the value cannot be found.
// Will iterate through the entire sequence.
func LastIndexOf[T comparable](collection iter.Seq[T], element T) int {
index := -1
var i int
for item := range collection {
if item == element {
index = i
}
i++
}
return index
}
// HasPrefix returns true if the collection has the prefix.
// Will iterate at most the size of prefix.
func HasPrefix[T comparable](collection iter.Seq[T], prefix ...T) bool {
if len(prefix) == 0 {
return true
}
var i int
for item := range collection {
if item != prefix[i] {
return false
}
i++
if i == len(prefix) {
return true
}
}
return false
}
// HasSuffix returns true if the collection has the suffix.
// Will iterate through the entire sequence and allocate a slice the size of suffix.
func HasSuffix[T comparable](collection iter.Seq[T], suffix ...T) bool {
if len(suffix) == 0 {
return true
}
n := len(suffix)
buf := make([]T, 0, n)
var i int
for item := range collection {
if len(buf) < n {
buf = append(buf, item)
} else {
buf[i] = item
}
i = (i + 1) % n
}
if len(buf) < n {
return false
}
i += n
for j := range suffix {
if suffix[j] != buf[(i+j)%n] {
return false
}
}
return true
}
// Find searches for an element in a sequence based on a predicate. Returns element and true if element was found.
// Will iterate through the entire sequence if predicate never returns true.
func Find[T any](collection iter.Seq[T], predicate func(item T) bool) (T, bool) {
return First(Filter(collection, predicate))
}
// FindIndexOf searches for an element in a sequence based on a predicate and returns the index and true.
// Returns -1 and false if the element is not found.
// Will iterate through the entire sequence if predicate never returns true.
func FindIndexOf[T any](collection iter.Seq[T], predicate func(item T) bool) (T, int, bool) {
var i int
for item := range collection {
if predicate(item) {
return item, i, true
}
i++
}
return lo.Empty[T](), -1, false
}
// FindLastIndexOf searches for the last element in a sequence based on a predicate and returns the index and true.
// Returns -1 and false if the element is not found.
// Will iterate through the entire sequence.
func FindLastIndexOf[T any](collection iter.Seq[T], predicate func(item T) bool) (T, int, bool) {
var result T
index := -1
var ok bool
var i int
for item := range collection {
if predicate(item) {
result = item
index = i
ok = true
}
i++
}
return result, index, ok
}
// FindOrElse searches for an element in a sequence based on a predicate. Returns the element if found or a given fallback value otherwise.
// Will iterate through the entire sequence if predicate never returns true.
func FindOrElse[T any](collection iter.Seq[T], fallback T, predicate func(item T) bool) T {
if result, ok := Find(collection, predicate); ok {
return result
}
return fallback
}
// FindUniques returns a sequence with all the elements that appear in the collection only once.
// The order of result values is determined by the order they occur in the collection.
// Will iterate through the entire sequence before yielding and allocate a map large enough to hold all distinct elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func FindUniques[T comparable, I ~func(func(T) bool)](collection I) I {
return FindUniquesBy(collection, func(item T) T { return item })
}
// FindUniquesBy returns a sequence with all the elements that appear in the collection only once.
// The order of result values is determined by the order they occur in the sequence. A transform function is
// invoked for each element in the sequence to generate the criterion by which uniqueness is computed.
// Will iterate through the entire sequence before yielding and allocate a map large enough to hold all distinct transformed elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func FindUniquesBy[T any, U comparable, I ~func(func(T) bool)](collection I, transform func(item T) U) I {
return func(yield func(T) bool) {
isDupl := make(map[U]bool)
for item := range collection {
key := transform(item)
if duplicated, ok := isDupl[key]; !ok {
isDupl[key] = false
} else if !duplicated {
isDupl[key] = true
}
}
for item := range collection {
key := transform(item)
if duplicated := isDupl[key]; !duplicated && !yield(item) {
return
}
}
}
}
// FindDuplicates returns a sequence with the first occurrence of each duplicated element in the collection.
// The order of result values is determined by the order duplicates occur in the collection.
// Will allocate a map large enough to hold all distinct elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func FindDuplicates[T comparable, I ~func(func(T) bool)](collection I) I {
return FindDuplicatesBy(collection, func(item T) T { return item })
}
// FindDuplicatesBy returns a sequence with the first occurrence of each duplicated element in the collection.
// The order of result values is determined by the order duplicates occur in the sequence. A transform function is
// invoked for each element in the sequence to generate the criterion by which uniqueness is computed.
// Will allocate a map large enough to hold all distinct transformed elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func FindDuplicatesBy[T any, U comparable, I ~func(func(T) bool)](collection I, transform func(item T) U) I {
return func(yield func(T) bool) {
isDupl := make(map[U]lo.Tuple2[T, bool])
for item := range collection {
key := transform(item)
if duplicated, ok := isDupl[key]; !ok {
isDupl[key] = lo.Tuple2[T, bool]{A: item}
} else if !duplicated.B {
if !yield(duplicated.A) {
return
}
isDupl[key] = lo.Tuple2[T, bool]{A: item, B: true}
}
}
}
}
// Min search the minimum value of a collection.
// Returns zero value when the collection is empty.
// Will iterate through the entire sequence.
func Min[T constraints.Ordered](collection iter.Seq[T]) T {
return MinBy(collection, func(a, b T) bool { return a < b })
}
// MinIndex search the minimum value of a collection and the index of the minimum value.
// Returns (zero value, -1) when the collection is empty.
// Will iterate through the entire sequence.
func MinIndex[T constraints.Ordered](collection iter.Seq[T]) (T, int) {
return MinIndexBy(collection, func(a, b T) bool { return a < b })
}
// MinBy search the minimum value of a collection using the given comparison function.
// If several values of the collection are equal to the smallest value, returns the first such value.
// Returns zero value when the collection is empty.
// Will iterate through the entire sequence.
func MinBy[T any](collection iter.Seq[T], comparison func(a, b T) bool) T {
first := true
var mIn T
for item := range collection {
if first {
mIn = item
first = false
} else if comparison(item, mIn) {
mIn = item
}
}
return mIn
}
// MinIndexBy search the minimum value of a collection using the given comparison function and the index of the minimum value.
// If several values of the collection are equal to the smallest value, returns the first such value.
// Returns (zero value, -1) when the collection is empty.
// Will iterate through the entire sequence.
func MinIndexBy[T any](collection iter.Seq[T], comparison func(a, b T) bool) (T, int) {
var mIn T
index := -1
var i int
for item := range collection {
if i == 0 || comparison(item, mIn) {
mIn = item
index = i
}
i++
}
return mIn, index
}
// Earliest search the minimum time.Time of a collection.
// Returns zero value when the collection is empty.
// Will iterate through the entire sequence.
func Earliest(times iter.Seq[time.Time]) time.Time {
return MinBy(times, func(a, b time.Time) bool { return a.Before(b) })
}
// EarliestBy search the minimum time.Time of a collection using the given transform function.
// Returns zero value when the collection is empty.
// Will iterate through the entire sequence.
func EarliestBy[T any](collection iter.Seq[T], transform func(item T) time.Time) T {
return MinBy(collection, func(a, b T) bool { return transform(a).Before(transform(b)) })
}
// Max searches the maximum value of a collection.
// Returns zero value when the collection is empty.
// Will iterate through the entire sequence.
func Max[T constraints.Ordered](collection iter.Seq[T]) T {
return MaxBy(collection, func(a, b T) bool { return a > b })
}
// MaxIndex searches the maximum value of a collection and the index of the maximum value.
// Returns (zero value, -1) when the collection is empty.
// Will iterate through the entire sequence.
func MaxIndex[T constraints.Ordered](collection iter.Seq[T]) (T, int) {
return MaxIndexBy(collection, func(a, b T) bool { return a > b })
}
// MaxBy search the maximum value of a collection using the given comparison function.
// If several values of the collection are equal to the greatest value, returns the first such value.
// Returns zero value when the collection is empty.
// Will iterate through the entire sequence.
func MaxBy[T any](collection iter.Seq[T], comparison func(a, b T) bool) T {
first := true
var mAx T
for item := range collection {
if first {
mAx = item
first = false
} else if comparison(item, mAx) {
mAx = item
}
}
return mAx
}
// MaxIndexBy search the maximum value of a collection using the given comparison function and the index of the maximum value.
// If several values of the collection are equal to the greatest value, returns the first such value.
// Returns (zero value, -1) when the collection is empty.
// Will iterate through the entire sequence.
func MaxIndexBy[T any](collection iter.Seq[T], comparison func(a, b T) bool) (T, int) {
var mAx T
index := -1
var i int
for item := range collection {
if i == 0 || comparison(item, mAx) {
mAx = item
index = i
}
i++
}
return mAx, index
}
// Latest search the maximum time.Time of a collection.
// Returns zero value when the collection is empty.
// Will iterate through the entire sequence.
func Latest(times iter.Seq[time.Time]) time.Time {
return MaxBy(times, func(a, b time.Time) bool { return a.After(b) })
}
// LatestBy search the maximum time.Time of a collection using the given transform function.
// Returns zero value when the collection is empty.
// Will iterate through the entire sequence.
func LatestBy[T any](collection iter.Seq[T], transform func(item T) time.Time) T {
return MaxBy(collection, func(a, b T) bool { return transform(a).After(transform(b)) })
}
// First returns the first element of a collection and check for availability of the first element.
// Will iterate at most once.
func First[T any](collection iter.Seq[T]) (T, bool) {
for item := range collection {
return item, true
}
return lo.Empty[T](), false
}
// FirstOrEmpty returns the first element of a collection or zero value if empty.
// Will iterate at most once.
func FirstOrEmpty[T any](collection iter.Seq[T]) T {
i, _ := First(collection)
return i
}
// FirstOr returns the first element of a collection or the fallback value if empty.
// Will iterate at most once.
func FirstOr[T any](collection iter.Seq[T], fallback T) T {
if i, ok := First(collection); ok {
return i
}
return fallback
}
// Last returns the last element of a collection or error if empty.
// Will iterate through the entire sequence.
func Last[T any](collection iter.Seq[T]) (T, bool) {
var t T
var ok bool
for item := range collection {
t = item
ok = true
}
return t, ok
}
// LastOrEmpty returns the last element of a collection or zero value if empty.
// Will iterate through the entire sequence.
func LastOrEmpty[T any](collection iter.Seq[T]) T {
i, _ := Last(collection)
return i
}
// LastOr returns the last element of a collection or the fallback value if empty.
// Will iterate through the entire sequence.
func LastOr[T any](collection iter.Seq[T], fallback T) T {
if i, ok := Last(collection); ok {
return i
}
return fallback
}
// Nth returns the element at index `nth` of collection. An error is returned when nth is out of bounds.
// Will iterate n times through the sequence.
func Nth[T any, N constraints.Integer](collection iter.Seq[T], nth N) (T, error) {
if nth >= 0 {
var i N
for item := range collection {
if i == nth {
return item, nil
}
i++
}
}
return lo.Empty[T](), fmt.Errorf("nth: %d out of bounds", nth)
}
// NthOr returns the element at index `nth` of collection.
// If `nth` is out of bounds, it returns the fallback value instead of an error.
// Will iterate n times through the sequence.
func NthOr[T any, N constraints.Integer](collection iter.Seq[T], nth N, fallback T) T {
value, err := Nth(collection, nth)
if err != nil {
return fallback
}
return value
}
// NthOrEmpty returns the element at index `nth` of collection.
// If `nth` is out of bounds, it returns the zero value (empty value) for that type.
// Will iterate n times through the sequence.
func NthOrEmpty[T any, N constraints.Integer](collection iter.Seq[T], nth N) T {
value, _ := Nth(collection, nth)
return value
}
// Sample returns a random item from collection.
// Will iterate through the entire sequence and allocate a slice large enough to hold all elements.
// Long input sequences can cause excessive memory usage.
func Sample[T any](collection iter.Seq[T]) T {
return SampleBy(collection, rand.IntN)
}
// SampleBy returns a random item from collection, using randomIntGenerator as the random index generator.
// Will iterate through the entire sequence and allocate a slice large enough to hold all elements.
// Long input sequences can cause excessive memory usage.
func SampleBy[T any](collection iter.Seq[T], randomIntGenerator func(int) int) T {
slice := slices.Collect(collection)
return lo.SampleBy(slice, randomIntGenerator)
}
// Samples returns N random unique items from collection.
// Will iterate through the entire sequence and allocate a slice large enough to hold all elements.
// Long input sequences can cause excessive memory usage.
func Samples[T any, I ~func(func(T) bool)](collection I, count int) I {
return SamplesBy(collection, count, rand.IntN)
}
// SamplesBy returns N random unique items from collection, using randomIntGenerator as the random index generator.
// Will iterate through the entire sequence and allocate a slice large enough to hold all elements.
// Long input sequences can cause excessive memory usage.
func SamplesBy[T any, I ~func(func(T) bool)](collection I, count int, randomIntGenerator func(int) int) I {
slice := slices.Collect(iter.Seq[T](collection))
seq := lo.SamplesBy(slice, count, randomIntGenerator)
return I(slices.Values(seq))
}
+582
View File
@@ -0,0 +1,582 @@
//go:build go1.23
package it
import (
"fmt"
"slices"
"time"
)
func ExampleIndexOf() {
list := slices.Values([]string{"foo", "bar", "baz"})
result := IndexOf(list, "bar")
fmt.Printf("%d", result)
// Output: 1
}
func ExampleIndexOf_notFound() {
list := slices.Values([]string{"foo", "bar", "baz"})
result := IndexOf(list, "qux")
fmt.Printf("%d", result)
// Output: -1
}
func ExampleLastIndexOf() {
list := slices.Values([]string{"foo", "bar", "baz", "bar"})
result := LastIndexOf(list, "bar")
fmt.Printf("%d", result)
// Output: 3
}
func ExampleLastIndexOf_notFound() {
list := slices.Values([]string{"foo", "bar", "baz"})
result := LastIndexOf(list, "qux")
fmt.Printf("%d", result)
// Output: -1
}
func ExampleFind() {
type User struct {
Name string
Age int
}
users := slices.Values([]User{
{Name: "Alice", Age: 25},
{Name: "Bob", Age: 30},
{Name: "Charlie", Age: 35},
})
result, found := Find(users, func(user User) bool {
return user.Age > 30
})
fmt.Printf("%s %t", result.Name, found)
// Output: Charlie true
}
func ExampleFind_notFound() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result, found := Find(list, func(n int) bool {
return n > 10
})
fmt.Printf("%d %t", result, found)
// Output: 0 false
}
func ExampleFindIndexOf() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result, index, found := FindIndexOf(list, func(n int) bool {
return n > 2
})
fmt.Printf("%d %d %t", result, index, found)
// Output: 3 2 true
}
func ExampleFindIndexOf_notFound() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result, index, found := FindIndexOf(list, func(n int) bool {
return n > 10
})
fmt.Printf("%d %d %t", result, index, found)
// Output: 0 -1 false
}
func ExampleFindLastIndexOf() {
list := slices.Values([]int{1, 2, 3, 4, 3, 5})
result, index, found := FindLastIndexOf(list, func(n int) bool {
return n == 3
})
fmt.Printf("%d %d %t", result, index, found)
// Output: 3 4 true
}
func ExampleFindLastIndexOf_notFound() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result, index, found := FindLastIndexOf(list, func(n int) bool {
return n > 10
})
fmt.Printf("%d %d %t", result, index, found)
// Output: 0 -1 false
}
func ExampleFindOrElse() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result := FindOrElse(list, -1, func(n int) bool {
return n > 10
})
fmt.Printf("%d", result)
// Output: -1
}
func ExampleFindOrElse_found() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result := FindOrElse(list, -1, func(n int) bool {
return n > 3
})
fmt.Printf("%d", result)
// Output: 4
}
func ExampleFindUniques() {
list := slices.Values([]int{1, 2, 2, 3, 3, 3, 4, 5})
result := FindUniques(list)
fmt.Printf("%v", slices.Collect(result))
// Output: [1 4 5]
}
func ExampleFindUniquesBy() {
type User struct {
Name string
Age int
}
users := slices.Values([]User{
{Name: "Alice", Age: 25},
{Name: "Bob", Age: 30},
{Name: "Charlie", Age: 25},
{Name: "David", Age: 30},
{Name: "Eve", Age: 35},
})
result := FindUniquesBy(users, func(user User) int {
return user.Age
})
fmt.Printf("%d", len(slices.Collect(result)))
// Output: 1
}
func ExampleFindDuplicates() {
list := slices.Values([]int{1, 2, 2, 3, 3, 3, 4, 5})
result := FindDuplicates(list)
fmt.Printf("%v", slices.Collect(result))
// Output: [2 3]
}
func ExampleFindDuplicatesBy() {
type User struct {
Name string
Age int
}
users := slices.Values([]User{
{Name: "Alice", Age: 25},
{Name: "Bob", Age: 30},
{Name: "Charlie", Age: 25},
{Name: "David", Age: 30},
{Name: "Eve", Age: 35},
})
result := FindDuplicatesBy(users, func(user User) int {
return user.Age
})
fmt.Printf("%d", len(slices.Collect(result)))
// Output: 2
}
func ExampleMin() {
list := slices.Values([]int{3, 1, 4, 1, 5, 9, 2, 6})
result := Min(list)
fmt.Printf("%d", result)
// Output: 1
}
func ExampleMin_empty() {
list := slices.Values([]int{})
result := Min(list)
fmt.Printf("%d", result)
// Output: 0
}
func ExampleMinIndex() {
list := slices.Values([]int{3, 1, 4, 1, 5, 9, 2, 6})
result, index := MinIndex(list)
fmt.Printf("%d %d", result, index)
// Output: 1 1
}
func ExampleMinIndex_empty() {
list := slices.Values([]int{})
result, index := MinIndex(list)
fmt.Printf("%d %d", result, index)
// Output: 0 -1
}
func ExampleMinBy() {
type User struct {
Name string
Age int
}
users := slices.Values([]User{
{Name: "Alice", Age: 25},
{Name: "Bob", Age: 30},
{Name: "Charlie", Age: 35},
})
result := MinBy(users, func(a, b User) bool {
return a.Age < b.Age
})
fmt.Printf("%s", result.Name)
// Output: Alice
}
func ExampleMinIndexBy() {
type User struct {
Name string
Age int
}
users := slices.Values([]User{
{Name: "Alice", Age: 25},
{Name: "Bob", Age: 30},
{Name: "Charlie", Age: 35},
})
result, index := MinIndexBy(users, func(a, b User) bool {
return a.Age < b.Age
})
fmt.Printf("%s %d", result.Name, index)
// Output: Alice 0
}
func ExampleEarliest() {
now := time.Now()
past := now.Add(-time.Hour)
future := now.Add(time.Hour)
result := Earliest(slices.Values([]time.Time{future, now, past}))
fmt.Printf("%t", result.Equal(past))
// Output: true
}
func ExampleEarliestBy() {
type Event struct {
Name string
Time time.Time
}
now := time.Now()
events := slices.Values([]Event{
{Name: "Event A", Time: now.Add(time.Hour)},
{Name: "Event B", Time: now},
{Name: "Event C", Time: now.Add(-time.Hour)},
})
result := EarliestBy(events, func(event Event) time.Time {
return event.Time
})
fmt.Printf("%s", result.Name)
// Output: Event C
}
func ExampleMax() {
list := slices.Values([]int{3, 1, 4, 1, 5, 9, 2, 6})
result := Max(list)
fmt.Printf("%d", result)
// Output: 9
}
func ExampleMax_empty() {
list := slices.Values([]int{})
result := Max(list)
fmt.Printf("%d", result)
// Output: 0
}
func ExampleMaxIndex() {
list := slices.Values([]int{3, 1, 4, 1, 5, 9, 2, 6})
result, index := MaxIndex(list)
fmt.Printf("%d %d", result, index)
// Output: 9 5
}
func ExampleMaxIndex_empty() {
list := slices.Values([]int{})
result, index := MaxIndex(list)
fmt.Printf("%d %d", result, index)
// Output: 0 -1
}
func ExampleMaxBy() {
type User struct {
Name string
Age int
}
users := slices.Values([]User{
{Name: "Alice", Age: 25},
{Name: "Bob", Age: 30},
{Name: "Charlie", Age: 35},
})
result := MaxBy(users, func(a, b User) bool {
return a.Age > b.Age
})
fmt.Printf("%s", result.Name)
// Output: Charlie
}
func ExampleMaxIndexBy() {
type User struct {
Name string
Age int
}
users := slices.Values([]User{
{Name: "Alice", Age: 25},
{Name: "Bob", Age: 30},
{Name: "Charlie", Age: 35},
})
result, index := MaxIndexBy(users, func(a, b User) bool {
return a.Age > b.Age
})
fmt.Printf("%s %d", result.Name, index)
// Output: Charlie 2
}
func ExampleLatest() {
now := time.Now()
past := now.Add(-time.Hour)
future := now.Add(time.Hour)
result := Latest(slices.Values([]time.Time{future, now, past}))
fmt.Printf("%t", result.Equal(future))
// Output: true
}
func ExampleLatestBy() {
type Event struct {
Name string
Time time.Time
}
now := time.Now()
events := slices.Values([]Event{
{Name: "Event A", Time: now.Add(time.Hour)},
{Name: "Event B", Time: now},
{Name: "Event C", Time: now.Add(-time.Hour)},
})
result := LatestBy(events, func(event Event) time.Time {
return event.Time
})
fmt.Printf("%s", result.Name)
// Output: Event A
}
func ExampleFirst() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result, found := First(list)
fmt.Printf("%d %t", result, found)
// Output: 1 true
}
func ExampleFirst_empty() {
list := slices.Values([]int{})
result, found := First(list)
fmt.Printf("%d %t", result, found)
// Output: 0 false
}
func ExampleFirstOrEmpty() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result := FirstOrEmpty(list)
fmt.Printf("%d", result)
// Output: 1
}
func ExampleFirstOrEmpty_empty() {
list := slices.Values([]int{})
result := FirstOrEmpty(list)
fmt.Printf("%d", result)
// Output: 0
}
func ExampleFirstOr() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result := FirstOr(list, -1)
fmt.Printf("%d", result)
// Output: 1
}
func ExampleFirstOr_empty() {
list := slices.Values([]int{})
result := FirstOr(list, -1)
fmt.Printf("%d", result)
// Output: -1
}
func ExampleLast() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result, found := Last(list)
fmt.Printf("%d %t", result, found)
// Output: 5 true
}
func ExampleLast_empty() {
list := slices.Values([]int{})
result, found := Last(list)
fmt.Printf("%d %t", result, found)
// Output: 0 false
}
func ExampleLastOrEmpty() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result := LastOrEmpty(list)
fmt.Printf("%d", result)
// Output: 5
}
func ExampleLastOrEmpty_empty() {
list := slices.Values([]int{})
result := LastOrEmpty(list)
fmt.Printf("%d", result)
// Output: 0
}
func ExampleLastOr() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result := LastOr(list, -1)
fmt.Printf("%d", result)
// Output: 5
}
func ExampleLastOr_empty() {
list := slices.Values([]int{})
result := LastOr(list, -1)
fmt.Printf("%d", result)
// Output: -1
}
func ExampleNth() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result, err := Nth(list, 2)
fmt.Printf("%d %v", result, err)
// Output: 3 <nil>
}
func ExampleNth_outOfBounds() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result, err := Nth(list, 10)
fmt.Printf("%d %v", result, err)
// Output: 0 nth: 10 out of bounds
}
func ExampleNthOr() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result := NthOr(list, 2, -1)
fmt.Printf("%d", result)
// Output: 3
}
func ExampleNthOr_outOfBounds() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result := NthOr(list, 10, -1)
fmt.Printf("%d", result)
// Output: -1
}
func ExampleNthOrEmpty() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result := NthOrEmpty(list, 2)
fmt.Printf("%d", result)
// Output: 3
}
func ExampleNthOrEmpty_outOfBounds() {
list := slices.Values([]int{1, 2, 3, 4, 5})
result := NthOrEmpty(list, 10)
fmt.Printf("%d", result)
// Output: 0
}
+727
View File
@@ -0,0 +1,727 @@
//go:build go1.23
package it
import (
"iter"
"math/rand/v2"
"slices"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestIndexOf(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := IndexOf(values(0, 1, 2, 1, 2, 3), 2)
result2 := IndexOf(values(0, 1, 2, 1, 2, 3), 6)
is.Equal(2, result1)
is.Equal(-1, result2)
}
func TestLastIndexOf(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := LastIndexOf(values(0, 1, 2, 1, 2, 3), 2)
result2 := LastIndexOf(values(0, 1, 2, 1, 2, 3), 6)
is.Equal(4, result1)
is.Equal(-1, result2)
}
func TestHasPrefix(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.True(HasPrefix(values(1, 2, 3, 4), 1, 2))
is.False(HasPrefix(values(1, 2, 3, 4), 42))
is.False(HasPrefix(values(1, 2), 1, 2, 3, 4))
is.True(HasPrefix(values(1, 2, 3, 4)))
}
func TestHasSuffix(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.True(HasSuffix(values(1, 2, 3, 4), 3, 4))
is.True(HasSuffix(values(1, 2, 3, 4, 5), 3, 4, 5))
is.False(HasSuffix(values(1, 2, 3, 4), 42))
is.False(HasSuffix(values(1, 2), 1, 2, 3, 4))
is.True(HasSuffix(values(1, 2, 3, 4)))
}
func TestFind(t *testing.T) {
t.Parallel()
is := assert.New(t)
index := 0
result1, ok1 := Find(values("a", "b", "c", "d"), func(item string) bool {
is.Equal([]string{"a", "b", "c", "d"}[index], item)
index++
return item == "b"
})
result2, ok2 := Find(values("foobar"), func(item string) bool {
is.Equal("foobar", item)
return item == "b"
})
is.True(ok1)
is.Equal("b", result1)
is.False(ok2)
is.Empty(result2)
}
func TestFindIndexOf(t *testing.T) {
t.Parallel()
is := assert.New(t)
index := 0
item1, index1, ok1 := FindIndexOf(values("a", "b", "c", "d", "b"), func(item string) bool {
is.Equal([]string{"a", "b", "c", "d", "b"}[index], item)
index++
return item == "b"
})
item2, index2, ok2 := FindIndexOf(values("foobar"), func(item string) bool {
is.Equal("foobar", item)
return item == "b"
})
is.Equal("b", item1)
is.True(ok1)
is.Equal(1, index1)
is.Empty(item2)
is.False(ok2)
is.Equal(-1, index2)
}
func TestFindLastIndexOf(t *testing.T) {
t.Parallel()
is := assert.New(t)
item1, index1, ok1 := FindLastIndexOf(values("a", "b", "c", "d", "b"), func(item string) bool {
return item == "b"
})
item2, index2, ok2 := FindLastIndexOf(values("foobar"), func(item string) bool {
return item == "b"
})
is.Equal("b", item1)
is.True(ok1)
is.Equal(4, index1)
is.Empty(item2)
is.False(ok2)
is.Equal(-1, index2)
}
func TestFindOrElse(t *testing.T) {
t.Parallel()
is := assert.New(t)
index := 0
result1 := FindOrElse(values("a", "b", "c", "d"), "x", func(item string) bool {
is.Equal([]string{"a", "b", "c", "d"}[index], item)
index++
return item == "b"
})
result2 := FindOrElse(values("foobar"), "x", func(item string) bool {
is.Equal("foobar", item)
return item == "b"
})
is.Equal("b", result1)
is.Equal("x", result2)
}
func TestFindUniques(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := FindUniques(values(1, 2, 3))
is.Equal([]int{1, 2, 3}, slices.Collect(result1))
result2 := FindUniques(values(1, 2, 2, 3, 1, 2))
is.Equal([]int{3}, slices.Collect(result2))
result3 := FindUniques(values(1, 2, 2, 1))
is.Empty(slices.Collect(result3))
result4 := FindUniques(values[int]())
is.Empty(slices.Collect(result4))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := FindUniques(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestFindUniquesBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := FindUniquesBy(values(0, 1, 2), func(i int) int {
return i % 3
})
is.Equal([]int{0, 1, 2}, slices.Collect(result1))
result2 := FindUniquesBy(values(0, 1, 2, 3, 4), func(i int) int {
return i % 3
})
is.Equal([]int{2}, slices.Collect(result2))
result3 := FindUniquesBy(values(0, 1, 2, 3, 4, 5), func(i int) int {
return i % 3
})
is.Empty(slices.Collect(result3))
result4 := FindUniquesBy(values[int](), func(i int) int {
return i % 3
})
is.Empty(slices.Collect(result4))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := FindUniquesBy(allStrings, func(i string) string {
return i
})
is.IsType(nonempty, allStrings, "type preserved")
}
func TestFindDuplicates(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := FindDuplicates(values(1, 2, 2, 1, 2, 3))
is.Equal([]int{2, 1}, slices.Collect(result1))
result2 := FindDuplicates(values(1, 2, 3))
is.Empty(slices.Collect(result2))
result3 := FindDuplicates(values[int]())
is.Empty(slices.Collect(result3))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := FindDuplicates(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestFindDuplicatesBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := FindDuplicatesBy(values(3, 4, 5, 6, 7), func(i int) int {
return i % 3
})
is.Equal([]int{3, 4}, slices.Collect(result1))
result2 := FindDuplicatesBy(values(0, 1, 2, 3, 4), func(i int) int {
return i % 5
})
is.Empty(slices.Collect(result2))
result3 := FindDuplicatesBy(values[int](), func(i int) int {
return i % 3
})
is.Empty(slices.Collect(result3))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := FindDuplicatesBy(allStrings, func(i string) string {
return i
})
is.IsType(nonempty, allStrings, "type preserved")
}
func TestMin(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Min(values(1, 2, 3))
result2 := Min(values(3, 2, 1))
result3 := Min(values(time.Second, time.Minute, time.Hour))
result4 := Min(values[int]())
is.Equal(1, result1)
is.Equal(1, result2)
is.Equal(time.Second, result3)
is.Zero(result4)
}
func TestMinIndex(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1, index1 := MinIndex(values(1, 2, 3))
result2, index2 := MinIndex(values(3, 2, 1))
result3, index3 := MinIndex(values(time.Second, time.Minute, time.Hour))
result4, index4 := MinIndex(values[int]())
is.Equal(1, result1)
is.Zero(index1)
is.Equal(1, result2)
is.Equal(2, index2)
is.Equal(time.Second, result3)
is.Zero(index3)
is.Zero(result4)
is.Equal(-1, index4)
}
func TestMinBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := MinBy(values("s1", "string2", "s3"), func(item, mIn string) bool {
return len(item) < len(mIn)
})
result2 := MinBy(values("string1", "string2", "s3"), func(item, mIn string) bool {
return len(item) < len(mIn)
})
result3 := MinBy(values[string](), func(item, mIn string) bool {
return len(item) < len(mIn)
})
is.Equal("s1", result1)
is.Equal("s3", result2)
is.Empty(result3)
}
func TestMinIndexBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1, index1 := MinIndexBy(values("s1", "string2", "s3"), func(item, mIn string) bool {
return len(item) < len(mIn)
})
result2, index2 := MinIndexBy(values("string1", "string2", "s3"), func(item, mIn string) bool {
return len(item) < len(mIn)
})
result3, index3 := MinIndexBy(values[string](), func(item, mIn string) bool {
return len(item) < len(mIn)
})
is.Equal("s1", result1)
is.Zero(index1)
is.Equal("s3", result2)
is.Equal(2, index2)
is.Empty(result3)
is.Equal(-1, index3)
}
func TestEarliest(t *testing.T) {
t.Parallel()
is := assert.New(t)
a := time.Now()
b := a.Add(time.Hour)
result1 := Earliest(values(a, b))
result2 := Earliest(values[time.Time]())
is.Equal(a, result1)
is.Zero(result2)
}
func TestEarliestBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
type foo struct {
bar time.Time
}
t1 := time.Now()
t2 := t1.Add(time.Hour)
t3 := t1.Add(-time.Hour)
result1 := EarliestBy(values(foo{t1}, foo{t2}, foo{t3}), func(i foo) time.Time {
return i.bar
})
result2 := EarliestBy(values(foo{t1}), func(i foo) time.Time {
return i.bar
})
result3 := EarliestBy(values[foo](), func(i foo) time.Time {
return i.bar
})
is.Equal(foo{t3}, result1)
is.Equal(foo{t1}, result2)
is.Zero(result3)
}
func TestMax(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Max(values(1, 2, 3))
result2 := Max(values(3, 2, 1))
result3 := Max(values(time.Second, time.Minute, time.Hour))
result4 := Max(values[int]())
is.Equal(3, result1)
is.Equal(3, result2)
is.Equal(time.Hour, result3)
is.Zero(result4)
}
func TestMaxIndex(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1, index1 := MaxIndex(values(1, 2, 3))
result2, index2 := MaxIndex(values(3, 2, 1))
result3, index3 := MaxIndex(values(time.Second, time.Minute, time.Hour))
result4, index4 := MaxIndex(values[int]())
is.Equal(3, result1)
is.Equal(2, index1)
is.Equal(3, result2)
is.Zero(index2)
is.Equal(time.Hour, result3)
is.Equal(2, index3)
is.Zero(result4)
is.Equal(-1, index4)
}
func TestMaxBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := MaxBy(values("s1", "string2", "s3"), func(item, mAx string) bool {
return len(item) > len(mAx)
})
result2 := MaxBy(values("string1", "string2", "s3"), func(item, mAx string) bool {
return len(item) > len(mAx)
})
result3 := MaxBy(values[string](), func(item, mAx string) bool {
return len(item) > len(mAx)
})
is.Equal("string2", result1)
is.Equal("string1", result2)
is.Empty(result3)
}
func TestMaxIndexBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1, index1 := MaxIndexBy(values("s1", "string2", "s3"), func(item, mAx string) bool {
return len(item) > len(mAx)
})
result2, index2 := MaxIndexBy(values("string1", "string2", "s3"), func(item, mAx string) bool {
return len(item) > len(mAx)
})
result3, index3 := MaxIndexBy(values[string](), func(item, mAx string) bool {
return len(item) > len(mAx)
})
is.Equal("string2", result1)
is.Equal(1, index1)
is.Equal("string1", result2)
is.Zero(index2)
is.Empty(result3)
is.Equal(-1, index3)
}
func TestLatest(t *testing.T) {
t.Parallel()
is := assert.New(t)
a := time.Now()
b := a.Add(time.Hour)
result1 := Latest(values(a, b))
result2 := Latest(values[time.Time]())
is.Equal(b, result1)
is.Zero(result2)
}
func TestLatestBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
type foo struct {
bar time.Time
}
t1 := time.Now()
t2 := t1.Add(time.Hour)
t3 := t1.Add(-time.Hour)
result1 := LatestBy(values(foo{t1}, foo{t2}, foo{t3}), func(i foo) time.Time {
return i.bar
})
result2 := LatestBy(values(foo{t1}), func(i foo) time.Time {
return i.bar
})
result3 := LatestBy(values[foo](), func(i foo) time.Time {
return i.bar
})
is.Equal(foo{t2}, result1)
is.Equal(foo{t1}, result2)
is.Zero(result3)
}
func TestFirst(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1, ok1 := First(values(1, 2, 3))
result2, ok2 := First(values[int]())
is.Equal(1, result1)
is.True(ok1)
is.Zero(result2)
is.False(ok2)
}
func TestFirstOrEmpty(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := FirstOrEmpty(values(1, 2, 3))
result2 := FirstOrEmpty(values[int]())
result3 := FirstOrEmpty(values[string]())
is.Equal(1, result1)
is.Zero(result2)
is.Empty(result3)
}
func TestFirstOr(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := FirstOr(values(1, 2, 3), 63)
result2 := FirstOr(values[int](), 23)
result3 := FirstOr(values[string](), "test")
is.Equal(1, result1)
is.Equal(23, result2)
is.Equal("test", result3)
}
func TestLast(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1, ok1 := Last(values(1, 2, 3))
result2, ok2 := Last(values[int]())
is.Equal(3, result1)
is.True(ok1)
is.Zero(result2)
is.False(ok2)
}
func TestLastOrEmpty(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := LastOrEmpty(values(1, 2, 3))
result2 := LastOrEmpty(values[int]())
result3 := LastOrEmpty(values[string]())
is.Equal(3, result1)
is.Zero(result2)
is.Empty(result3)
}
func TestLastOr(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := LastOr(values(1, 2, 3), 63)
result2 := LastOr(values[int](), 23)
result3 := LastOr(values[string](), "test")
is.Equal(3, result1)
is.Equal(23, result2)
is.Equal("test", result3)
}
func TestNth(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1, err1 := Nth(values(0, 1, 2, 3), 2)
result2, err2 := Nth(values(0, 1, 2, 3), -2)
result3, err3 := Nth(values(0, 1, 2, 3), 42)
result4, err4 := Nth(values[int](), 0)
result5, err5 := Nth(values(42), 0)
result6, err6 := Nth(values(42), -1)
is.Equal(2, result1)
is.NoError(err1)
is.Zero(result2)
is.EqualError(err2, "nth: -2 out of bounds")
is.Zero(result3)
is.EqualError(err3, "nth: 42 out of bounds")
is.Zero(result4)
is.EqualError(err4, "nth: 0 out of bounds")
is.Equal(42, result5)
is.NoError(err5)
is.Zero(result6)
is.EqualError(err6, "nth: -1 out of bounds")
}
func TestNthOr(t *testing.T) {
t.Parallel()
t.Run("Integers", func(t *testing.T) {
t.Parallel()
is := assert.New(t)
const defaultValue = -1
ints := values(10, 20, 30, 40, 50)
is.Equal(30, NthOr(ints, 2, defaultValue))
is.Equal(defaultValue, NthOr(ints, -1, defaultValue))
is.Equal(defaultValue, NthOr(ints, 5, defaultValue))
})
t.Run("Strings", func(t *testing.T) {
t.Parallel()
is := assert.New(t)
const defaultValue = "none"
strs := values("apple", "banana", "cherry", "date")
is.Equal("banana", NthOr(strs, 1, defaultValue)) // Index 1, expected "banana"
is.Equal(defaultValue, NthOr(strs, -2, defaultValue)) // Negative index -2, expected "cherry"
is.Equal(defaultValue, NthOr(strs, 10, defaultValue)) // Out of bounds, fallback "none"
})
t.Run("Structs", func(t *testing.T) {
t.Parallel()
is := assert.New(t)
type User struct {
ID int
Name string
}
users := values(
User{ID: 1, Name: "Alice"},
User{ID: 2, Name: "Bob"},
User{ID: 3, Name: "Charlie"},
)
defaultValue := User{ID: 0, Name: "Unknown"}
is.Equal(User{ID: 1, Name: "Alice"}, NthOr(users, 0, defaultValue))
is.Equal(defaultValue, NthOr(users, -1, defaultValue))
is.Equal(defaultValue, NthOr(users, 10, defaultValue))
})
}
func TestNthOrEmpty(t *testing.T) {
t.Parallel()
t.Run("Integers", func(t *testing.T) {
t.Parallel()
is := assert.New(t)
ints := values(10, 20, 30, 40, 50)
is.Equal(30, NthOrEmpty(ints, 2))
is.Zero(NthOrEmpty(ints, -1))
is.Zero(NthOrEmpty(ints, 10))
})
t.Run("Strings", func(t *testing.T) {
t.Parallel()
is := assert.New(t)
strs := values("apple", "banana", "cherry", "date")
is.Equal("banana", NthOrEmpty(strs, 1))
is.Empty(NthOrEmpty(strs, -2))
is.Empty(NthOrEmpty(strs, 10))
})
t.Run("Structs", func(t *testing.T) {
t.Parallel()
is := assert.New(t)
type User struct {
ID int
Name string
}
users := values(
User{ID: 1, Name: "Alice"},
User{ID: 2, Name: "Bob"},
User{ID: 3, Name: "Charlie"},
)
is.Equal(User{ID: 1, Name: "Alice"}, NthOrEmpty(users, 0))
is.Zero(NthOrEmpty(users, -1))
is.Zero(NthOrEmpty(users, 10))
})
}
func TestSample(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Sample(values("a", "b", "c"))
result2 := Sample(values[string]())
is.True(Contains(values("a", "b", "c"), result1))
is.Empty(result2)
}
func TestSampleBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := SampleBy(values("a", "b", "c"), rand.IntN)
result2 := SampleBy(values[string](), rand.IntN)
is.True(Contains(values("a", "b", "c"), result1))
is.Empty(result2)
}
func TestSamples(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Samples(values("a", "b", "c"), 3)
result2 := Samples(values[string](), 3)
is.ElementsMatch(slices.Collect(result1), []string{"a", "b", "c"})
is.Empty(slices.Collect(result2))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := Samples(allStrings, 2)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestSamplesBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := SamplesBy(values("a", "b", "c"), 3, rand.IntN)
result2 := SamplesBy(values[string](), 3, rand.IntN)
is.ElementsMatch(slices.Collect(result1), []string{"a", "b", "c"})
is.Empty(slices.Collect(result2))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := SamplesBy(allStrings, 2, rand.IntN)
is.IsType(nonempty, allStrings, "type preserved")
}
+206
View File
@@ -0,0 +1,206 @@
//go:build go1.23
package it
import (
"iter"
"github.com/samber/lo"
)
// Contains returns true if an element is present in a collection.
// Will iterate through the entire sequence if element is not found.
func Contains[T comparable](collection iter.Seq[T], element T) bool {
return ContainsBy(collection, func(item T) bool { return item == element })
}
// ContainsBy returns true if predicate function return true.
// Will iterate through the entire sequence if predicate never returns true.
func ContainsBy[T any](collection iter.Seq[T], predicate func(item T) bool) bool {
return IsNotEmpty(Filter(collection, predicate))
}
// Every returns true if all elements of a subset are contained in a collection or if the subset is empty.
// Will iterate through the entire sequence if subset elements always match.
func Every[T comparable](collection iter.Seq[T], subset ...T) bool {
if len(subset) == 0 {
return true
}
set := lo.Keyify(subset)
for item := range collection {
if _, ok := set[item]; ok {
delete(set, item)
if len(set) == 0 {
return true
}
}
}
return false
}
// EveryBy returns true if the predicate returns true for all elements in the collection or if the collection is empty.
// Will iterate through the entire sequence if predicate never returns false.
func EveryBy[T any](collection iter.Seq[T], predicate func(item T) bool) bool {
return IsEmpty(Reject(collection, predicate))
}
// Some returns true if at least 1 element of a subset is contained in a collection.
// If the subset is empty Some returns false.
// Will iterate through the entire sequence if subset elements never match.
func Some[T comparable](collection iter.Seq[T], subset ...T) bool {
return SomeBy(collection, func(item T) bool { return lo.Contains(subset, item) })
}
// SomeBy returns true if the predicate returns true for any of the elements in the collection.
// If the collection is empty SomeBy returns false.
// Will iterate through the entire sequence if predicate never returns true.
func SomeBy[T any](collection iter.Seq[T], predicate func(item T) bool) bool {
return IsNotEmpty(Filter(collection, predicate))
}
// None returns true if no element of a subset is contained in a collection or if the subset is empty.
// Will iterate through the entire sequence if subset elements never match.
func None[T comparable](collection iter.Seq[T], subset ...T) bool {
return NoneBy(collection, func(item T) bool { return lo.Contains(subset, item) })
}
// NoneBy returns true if the predicate returns true for none of the elements in the collection or if the collection is empty.
// Will iterate through the entire sequence if predicate never returns true.
func NoneBy[T any](collection iter.Seq[T], predicate func(item T) bool) bool {
return IsEmpty(Filter(collection, predicate))
}
// Intersect returns the intersection between given collections.
// Will allocate a map large enough to hold all distinct elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func Intersect[T comparable, I ~func(func(T) bool)](lists ...I) I { //nolint:gocyclo
if len(lists) == 0 {
return I(Empty[T]())
}
if len(lists) == 1 {
return lists[0]
}
return func(yield func(T) bool) {
seen := make(map[T]bool)
for i := len(lists) - 1; i >= 0; i-- {
if i == len(lists)-1 {
for item := range lists[i] {
seen[item] = true
}
continue
}
if i == 0 {
for item := range lists[0] {
if _, ok := seen[item]; ok {
if !yield(item) {
return
}
delete(seen, item)
}
}
continue
}
for k := range seen {
seen[k] = false
}
for item := range lists[i] {
if _, ok := seen[item]; ok {
seen[item] = true
}
}
for k, v := range seen {
if !v {
delete(seen, k)
}
}
if len(seen) == 0 {
return
}
}
}
}
// Union returns all distinct elements from given collections.
// Will allocate a map large enough to hold all distinct elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func Union[T comparable, I ~func(func(T) bool)](lists ...I) I {
return func(yield func(T) bool) {
seen := make(map[T]struct{})
for i := range lists {
for item := range lists[i] {
if _, ok := seen[item]; !ok {
if !yield(item) {
return
}
seen[item] = struct{}{}
}
}
}
}
}
// Without returns a sequence excluding all given values.
// Will allocate a map large enough to hold all distinct excludes.
func Without[T comparable, I ~func(func(T) bool)](collection I, exclude ...T) I {
return WithoutBy(collection, func(item T) T { return item }, exclude...)
}
// WithoutBy filters a sequence by excluding elements whose extracted keys match any in the exclude list.
// Returns a sequence containing only the elements whose keys are not in the exclude list.
// Will allocate a map large enough to hold all distinct excludes.
func WithoutBy[T any, K comparable, I ~func(func(T) bool)](collection I, transform func(item T) K, exclude ...K) I {
set := lo.Keyify(exclude)
return Reject(collection, func(item T) bool { return lo.HasKey(set, transform(item)) })
}
// WithoutNth returns a sequence excluding the nth value.
// Will allocate a map large enough to hold all distinct nths.
func WithoutNth[T comparable, I ~func(func(T) bool)](collection I, nths ...int) I {
set := lo.Keyify(nths)
return RejectI(collection, func(_ T, index int) bool { return lo.HasKey(set, index) })
}
// ElementsMatch returns true if lists contain the same set of elements (including empty set).
// If there are duplicate elements, the number of occurrences in each list should match.
// The order of elements is not checked.
// Will iterate through each sequence before returning and allocate a map large enough to hold all distinct elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func ElementsMatch[T comparable](list1, list2 iter.Seq[T]) bool {
return ElementsMatchBy(list1, list2, func(item T) T { return item })
}
// ElementsMatchBy returns true if lists contain the same set of elements' keys (including empty set).
// If there are duplicate keys, the number of occurrences in each list should match.
// The order of elements is not checked.
// Will iterate through each sequence before returning and allocate a map large enough to hold all distinct transformed elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func ElementsMatchBy[T any, K comparable](list1, list2 iter.Seq[T], transform func(item T) K) bool {
counters := make(map[K]int)
for item := range list1 {
counters[transform(item)]++
}
for item := range list2 {
counters[transform(item)]--
}
for _, count := range counters {
if count != 0 {
return false
}
}
return true
}
+37
View File
@@ -0,0 +1,37 @@
//go:build go1.23
package it
import (
"fmt"
"slices"
)
func ExampleWithoutBy() {
type User struct {
ID int
Name string
}
// original users
users := values(
User{ID: 1, Name: "Alice"},
User{ID: 2, Name: "Bob"},
User{ID: 3, Name: "Charlie"},
)
// exclude users with IDs 2 and 3
excludedIDs := []int{2, 3}
// extract function to get the user ID
extractID := func(user User) int {
return user.ID
}
// filtering users
filteredUsers := WithoutBy(users, extractID, excludedIDs...)
// output the filtered users
fmt.Printf("%v", slices.Collect(filteredUsers))
// Output:
// [{1 Alice}]
}
+339
View File
@@ -0,0 +1,339 @@
//go:build go1.23
package it
import (
"iter"
"slices"
"testing"
"github.com/stretchr/testify/assert"
)
func TestContains(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Contains(values(0, 1, 2, 3, 4, 5), 5)
result2 := Contains(values(0, 1, 2, 3, 4, 5), 6)
is.True(result1)
is.False(result2)
}
func TestContainsBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
type a struct {
A int
B string
}
a1 := values(a{A: 1, B: "1"}, a{A: 2, B: "2"}, a{A: 3, B: "3"})
result1 := ContainsBy(a1, func(t a) bool { return t.A == 1 && t.B == "2" })
result2 := ContainsBy(a1, func(t a) bool { return t.A == 2 && t.B == "2" })
a2 := values("aaa", "bbb", "ccc")
result3 := ContainsBy(a2, func(t string) bool { return t == "ccc" })
result4 := ContainsBy(a2, func(t string) bool { return t == "ddd" })
is.False(result1)
is.True(result2)
is.True(result3)
is.False(result4)
}
func TestEvery(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Every(values(0, 1, 2, 3, 4, 5), 0, 2)
result2 := Every(values(0, 1, 2, 3, 4, 5), 0, 6)
result3 := Every(values(0, 1, 2, 3, 4, 5), -1, 6)
result4 := Every(values(0, 1, 2, 3, 4, 5))
is.True(result1)
is.False(result2)
is.False(result3)
is.True(result4)
}
func TestEveryBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := EveryBy(values(1, 2, 3, 4), func(x int) bool {
return x < 5
})
is.True(result1)
result2 := EveryBy(values(1, 2, 3, 4), func(x int) bool {
return x < 3
})
is.False(result2)
result3 := EveryBy(values(1, 2, 3, 4), func(x int) bool {
return x < 0
})
is.False(result3)
result4 := EveryBy(values[int](), func(x int) bool {
return x < 5
})
is.True(result4)
}
func TestSome(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Some(values(0, 1, 2, 3, 4, 5), 0, 2)
result2 := Some(values(0, 1, 2, 3, 4, 5), 0, 6)
result3 := Some(values(0, 1, 2, 3, 4, 5), -1, 6)
result4 := Some(values(0, 1, 2, 3, 4, 5))
is.True(result1)
is.True(result2)
is.False(result3)
is.False(result4)
}
func TestSomeBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := SomeBy(values(1, 2, 3, 4), func(x int) bool {
return x < 5
})
is.True(result1)
result2 := SomeBy(values(1, 2, 3, 4), func(x int) bool {
return x < 3
})
is.True(result2)
result3 := SomeBy(values(1, 2, 3, 4), func(x int) bool {
return x < 0
})
is.False(result3)
result4 := SomeBy(values[int](), func(x int) bool {
return x < 5
})
is.False(result4)
}
func TestNone(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := None(values(0, 1, 2, 3, 4, 5), 0, 2)
result2 := None(values(0, 1, 2, 3, 4, 5), 0, 6)
result3 := None(values(0, 1, 2, 3, 4, 5), -1, 6)
result4 := None(values(0, 1, 2, 3, 4, 5))
is.False(result1)
is.False(result2)
is.True(result3)
is.True(result4)
}
func TestNoneBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := NoneBy(values(1, 2, 3, 4), func(x int) bool {
return x < 5
})
is.False(result1)
result2 := NoneBy(values(1, 2, 3, 4), func(x int) bool {
return x < 3
})
is.False(result2)
result3 := NoneBy(values(1, 2, 3, 4), func(x int) bool {
return x < 0
})
is.True(result3)
result4 := NoneBy(values[int](), func(x int) bool {
return x < 5
})
is.True(result4)
}
func TestIntersect(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Intersect([]iter.Seq[int]{}...)
result2 := Intersect(values(0, 1, 2, 3, 4, 5))
result3 := Intersect(values(0, 1, 2, 3, 4, 5), values(0, 6))
result4 := Intersect(values(0, 1, 2, 3, 4, 5), values(-1, 6))
result5 := Intersect(values(0, 6, 0), values(0, 1, 2, 3, 4, 5))
result6 := Intersect(values(0, 1, 2, 3, 4, 5), values(0, 6, 0))
result7 := Intersect(values(0, 1, 2), values(1, 2, 3), values(2, 3, 4))
result8 := Intersect(values(0, 1, 2), values(1, 2, 3), values(2, 3, 4), values(3, 4, 5))
result9 := Intersect(values(0, 1, 2), values(0, 1, 2), values(1, 2, 3), values(2, 3, 4), values(3, 4, 5))
is.Empty(slices.Collect(result1))
is.Equal([]int{0, 1, 2, 3, 4, 5}, slices.Collect(result2))
is.Equal([]int{0}, slices.Collect(result3))
is.Empty(slices.Collect(result4))
is.Equal([]int{0}, slices.Collect(result5))
is.Equal([]int{0}, slices.Collect(result6))
is.Equal([]int{2}, slices.Collect(result7))
is.Empty(slices.Collect(result8))
is.Empty(slices.Collect(result9))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := Intersect(allStrings, allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestUnion(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Union(values(0, 1, 2, 3, 4, 5), values(0, 2, 10))
result2 := Union(values(0, 1, 2, 3, 4, 5), values(6, 7))
result3 := Union(values(0, 1, 2, 3, 4, 5), values[int]())
result4 := Union(values(0, 1, 2), values(0, 1, 2, 3, 3))
result5 := Union(values(0, 1, 2), values(0, 1, 2))
result6 := Union(values[int](), values[int]())
is.Equal([]int{0, 1, 2, 3, 4, 5, 10}, slices.Collect(result1))
is.Equal([]int{0, 1, 2, 3, 4, 5, 6, 7}, slices.Collect(result2))
is.Equal([]int{0, 1, 2, 3, 4, 5}, slices.Collect(result3))
is.Equal([]int{0, 1, 2, 3}, slices.Collect(result4))
is.Equal([]int{0, 1, 2}, slices.Collect(result5))
is.Empty(slices.Collect(result6))
result11 := Union(values(0, 1, 2, 3, 4, 5), values(0, 2, 10), values(0, 1, 11))
result12 := Union(values(0, 1, 2, 3, 4, 5), values(6, 7), values(8, 9))
result13 := Union(values(0, 1, 2, 3, 4, 5), values[int](), values[int]())
result14 := Union(values(0, 1, 2), values(0, 1, 2), values(0, 1, 2))
result15 := Union(values[int](), values[int](), values[int]())
is.Equal([]int{0, 1, 2, 3, 4, 5, 10, 11}, slices.Collect(result11))
is.Equal([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, slices.Collect(result12))
is.Equal([]int{0, 1, 2, 3, 4, 5}, slices.Collect(result13))
is.Equal([]int{0, 1, 2}, slices.Collect(result14))
is.Empty(slices.Collect(result15))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := Union(allStrings, allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestWithout(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Without(values(0, 2, 10), 0, 1, 2, 3, 4, 5)
result2 := Without(values(0, 7), 0, 1, 2, 3, 4, 5)
result3 := Without(values[int](), 0, 1, 2, 3, 4, 5)
result4 := Without(values(0, 1, 2), 0, 1, 2)
result5 := Without(values[int]())
is.Equal([]int{10}, slices.Collect(result1))
is.Equal([]int{7}, slices.Collect(result2))
is.Empty(slices.Collect(result3))
is.Empty(slices.Collect(result4))
is.Empty(slices.Collect(result5))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := Without(allStrings, "")
is.IsType(nonempty, allStrings, "type preserved")
}
func TestWithoutBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
type User struct {
Name string
Age int
}
result1 := WithoutBy(values(User{Name: "nick"}, User{Name: "peter"}),
func(item User) string {
return item.Name
}, "nick", "lily")
result2 := WithoutBy(values[User](), func(item User) int { return item.Age }, 1, 2, 3)
result3 := WithoutBy(values[User](), func(item User) string { return item.Name })
is.Equal([]User{{Name: "peter"}}, slices.Collect(result1))
is.Empty(slices.Collect(result2))
is.Empty(slices.Collect(result3))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := WithoutBy(allStrings, func(string) string { return "" })
is.IsType(nonempty, allStrings, "type preserved")
}
func TestWithoutNth(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := WithoutNth(values(5, 6, 7), 1, 0)
is.Equal([]int{7}, slices.Collect(result1))
result2 := WithoutNth(values(1, 2))
is.Equal([]int{1, 2}, slices.Collect(result2))
result3 := WithoutNth(values[int]())
is.Empty(slices.Collect(result3))
result4 := WithoutNth(values(0, 1, 2, 3), -1, 4)
is.Equal([]int{0, 1, 2, 3}, slices.Collect(result4))
type myStrings iter.Seq[string]
allStrings := myStrings(values("", "foo", "bar"))
nonempty := WithoutNth(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestElementsMatch(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.False(ElementsMatch(values[int](), values(1)))
is.False(ElementsMatch(values(1), values(2)))
is.False(ElementsMatch(values(1), values(1, 2)))
is.False(ElementsMatch(values(1, 1, 2), values(2, 2, 1)))
is.True(ElementsMatch(values(1), values(1)))
is.True(ElementsMatch(values(1, 1), values(1, 1)))
is.True(ElementsMatch(values(1, 2), values(2, 1)))
is.True(ElementsMatch(values(1, 1, 2), values(1, 2, 1)))
}
func TestElementsMatchBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
type someType struct {
key string
}
is.True(ElementsMatchBy(
values(someType{key: "a"}, someType{key: "b"}),
values(someType{key: "b"}, someType{key: "a"}),
func(item someType) string { return item.key },
))
}
+18
View File
@@ -0,0 +1,18 @@
//go:build go1.23
package it
import (
"iter"
"slices"
)
func values[T any](v ...T) iter.Seq[T] { return slices.Values(v) }
type foo struct {
bar string
}
func (f foo) Clone() foo {
return foo{f.bar}
}
+245
View File
@@ -0,0 +1,245 @@
//go:build go1.23
package it
import (
"iter"
"maps"
)
// Keys creates a sequence of the map keys.
func Keys[K comparable, V any](in ...map[K]V) iter.Seq[K] {
return func(yield func(K) bool) {
for i := range in {
for k := range in[i] {
if !yield(k) {
return
}
}
}
}
}
// UniqKeys creates a sequence of unique keys in the map.
// Will allocate a map large enough to hold all distinct input keys.
// Long input sequences with heterogeneous keys can cause excessive memory usage.
func UniqKeys[K comparable, V any](in ...map[K]V) iter.Seq[K] {
return func(yield func(K) bool) {
seen := make(map[K]struct{})
for i := range in {
for k := range in[i] {
if _, ok := seen[k]; !ok {
if !yield(k) {
return
}
seen[k] = struct{}{}
}
}
}
}
}
// Values creates a sequence of the map values.
func Values[K comparable, V any](in ...map[K]V) iter.Seq[V] {
return func(yield func(V) bool) {
for i := range in {
for _, v := range in[i] {
if !yield(v) {
return
}
}
}
}
}
// UniqValues creates a sequence of unique values in the map.
// Will allocate a map large enough to hold all distinct input values.
// Long input sequences with heterogeneous values can cause excessive memory usage.
func UniqValues[K, V comparable](in ...map[K]V) iter.Seq[V] {
return func(yield func(V) bool) {
seen := make(map[V]struct{})
for i := range in {
for _, v := range in[i] {
if _, ok := seen[v]; !ok {
if !yield(v) {
return
}
seen[v] = struct{}{}
}
}
}
}
}
// Entries transforms a map into a sequence of key/value pairs.
func Entries[K comparable, V any](in ...map[K]V) iter.Seq2[K, V] {
return func(yield func(K, V) bool) {
for _, m := range in {
for k, v := range m {
if !yield(k, v) {
return
}
}
}
}
}
// ToPairs transforms a map into a sequence of key/value pairs.
// Alias of Entries().
func ToPairs[K comparable, V any](in ...map[K]V) iter.Seq2[K, V] {
return Entries(in...)
}
// FromEntries transforms a sequence of key/value pairs into a map.
func FromEntries[K comparable, V any](entries ...iter.Seq2[K, V]) map[K]V {
m := make(map[K]V)
for _, e := range entries {
maps.Insert(m, e)
}
return m
}
// FromPairs transforms a sequence of key/value pairs into a map.
// Alias of FromEntries().
func FromPairs[K comparable, V any](entries ...iter.Seq2[K, V]) map[K]V {
return FromEntries(entries...)
}
// Invert creates a sequence composed of inverted keys and values.
func Invert[K, V comparable](in iter.Seq2[K, V]) iter.Seq2[V, K] {
return func(yield func(V, K) bool) {
for k, v := range in {
if !yield(v, k) {
return
}
}
}
}
// Assign merges multiple sequences of maps from left to right.
func Assign[K comparable, V any, Map ~map[K]V](maps ...iter.Seq[Map]) Map {
out := make(Map)
for i := range maps {
for item := range maps[i] {
for k, v := range item {
out[k] = v
}
}
}
return out
}
// ChunkEntries splits a map into a sequence of elements in groups of length equal to its size. If the map cannot be split evenly,
// the final chunk will contain the remaining elements.
func ChunkEntries[K comparable, V any](m map[K]V, size int) iter.Seq[map[K]V] {
if size <= 0 {
panic("it.ChunkEntries: size must be greater than 0")
}
return func(yield func(map[K]V) bool) {
var result map[K]V
for k, v := range m {
if result == nil {
result = make(map[K]V, size)
}
result[k] = v
if len(result) == size {
if !yield(result) {
return
}
result = nil
}
}
if result != nil {
yield(result)
}
}
}
// MapToSeq transforms a map into a sequence based on specified transform.
func MapToSeq[K comparable, V, R any](in map[K]V, transform func(key K, value V) R) iter.Seq[R] {
return func(yield func(R) bool) {
for k, v := range in {
if !yield(transform(k, v)) {
return
}
}
}
}
// FilterMapToSeq transforms a map into a sequence based on specified transform.
// The transform returns a value and a boolean. If the boolean is true, the value is added to the result sequence.
// If the boolean is false, the value is not added to the result sequence.
// The order of the keys in the input map is not specified and the order of the keys in the output sequence is not guaranteed.
func FilterMapToSeq[K comparable, V, R any](in map[K]V, transform func(key K, value V) (R, bool)) iter.Seq[R] {
return func(yield func(R) bool) {
for k, v := range in {
if v, ok := transform(k, v); ok && !yield(v) {
return
}
}
}
}
// FilterKeys transforms a map into a sequence based on predicate returns true for specific elements.
// It is a mix of Filter and Keys.
func FilterKeys[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) iter.Seq[K] {
return func(yield func(K) bool) {
for k, v := range in {
if predicate(k, v) && !yield(k) {
return
}
}
}
}
// FilterValues transforms a map into a sequence based on predicate returns true for specific elements.
// It is a mix of Filter and Values.
func FilterValues[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) iter.Seq[V] {
return func(yield func(V) bool) {
for k, v := range in {
if predicate(k, v) && !yield(v) {
return
}
}
}
}
// SeqToSeq2 converts a sequence into a sequence of key-value pairs keyed by index.
func SeqToSeq2[T any](in iter.Seq[T]) iter.Seq2[int, T] {
return func(yield func(int, T) bool) {
var i int
for item := range in {
if !yield(i, item) {
return
}
i++
}
}
}
// Seq2KeyToSeq converts a sequence of key-value pairs into a sequence of keys.
func Seq2KeyToSeq[K, V any](in iter.Seq2[K, V]) iter.Seq[K] {
return func(yield func(K) bool) {
for k := range in {
if !yield(k) {
return
}
}
}
}
// Seq2ValueToSeq converts a sequence of key-value pairs into a sequence of values.
func Seq2ValueToSeq[K, V any](in iter.Seq2[K, V]) iter.Seq[V] {
return func(yield func(V) bool) {
for _, v := range in {
if !yield(v) {
return
}
}
}
}
+188
View File
@@ -0,0 +1,188 @@
//go:build go1.23
package it
import (
"fmt"
"maps"
"slices"
"sort"
)
func ExampleKeys() {
kv := map[string]int{"foo": 1, "bar": 2}
kv2 := map[string]int{"baz": 3}
result := slices.Collect(Keys(kv, kv2))
sort.Strings(result)
fmt.Printf("%v", result)
// Output: [bar baz foo]
}
func ExampleUniqKeys() {
kv := map[string]int{"foo": 1, "bar": 2}
kv2 := map[string]int{"bar": 3}
result := slices.Collect(UniqKeys(kv, kv2))
sort.Strings(result)
fmt.Printf("%v", result)
// Output: [bar foo]
}
func ExampleValues() {
kv := map[string]int{"foo": 1, "bar": 2}
kv2 := map[string]int{"baz": 3}
result := slices.Collect(Values(kv, kv2))
sort.Ints(result)
fmt.Printf("%v", result)
// Output: [1 2 3]
}
func ExampleUniqValues() {
kv := map[string]int{"foo": 1, "bar": 2}
kv2 := map[string]int{"baz": 2}
result := slices.Collect(UniqValues(kv, kv2))
sort.Ints(result)
fmt.Printf("%v", result)
// Output: [1 2]
}
func ExampleEntries() {
kv := map[string]int{"foo": 1, "bar": 2, "baz": 3}
result := maps.Collect(Entries(kv))
fmt.Printf("%v %v %v %v", len(result), result["foo"], result["bar"], result["baz"])
// Output: 3 1 2 3
}
func ExampleFromEntries() {
result := FromEntries(maps.All(map[string]int{
"foo": 1,
"bar": 2,
"baz": 3,
}))
fmt.Printf("%v %v %v %v", len(result), result["foo"], result["bar"], result["baz"])
// Output: 3 1 2 3
}
func ExampleInvert() {
kv := maps.All(map[string]int{"foo": 1, "bar": 2, "baz": 3})
result := maps.Collect(Invert(kv))
fmt.Printf("%v %v %v %v", len(result), result[1], result[2], result[3])
// Output: 3 foo bar baz
}
func ExampleAssign() {
result := Assign(values(
map[string]int{"a": 1, "b": 2},
map[string]int{"b": 3, "c": 4},
))
fmt.Printf("%v %v %v %v", len(result), result["a"], result["b"], result["c"])
// Output: 3 1 3 4
}
func ExampleChunkEntries() {
result := ChunkEntries(
map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 4,
"e": 5,
},
3,
)
for r := range result {
fmt.Printf("%d\n", len(r))
}
// Output:
// 3
// 2
}
func ExampleMapToSeq() {
kv := map[int]int64{1: 1, 2: 2, 3: 3, 4: 4}
result := slices.Collect(MapToSeq(kv, func(k int, v int64) string {
return fmt.Sprintf("%d_%d", k, v)
}))
sort.Strings(result)
fmt.Printf("%v", result)
// Output: [1_1 2_2 3_3 4_4]
}
func ExampleFilterMapToSeq() {
kv := map[int]int64{1: 1, 2: 2, 3: 3, 4: 4}
result := slices.Collect(FilterMapToSeq(kv, func(k int, v int64) (string, bool) {
return fmt.Sprintf("%d_%d", k, v), k%2 == 0
}))
sort.Strings(result)
fmt.Printf("%v", result)
// Output: [2_2 4_4]
}
func ExampleFilterKeys() {
kv := map[int]string{1: "foo", 2: "bar", 3: "baz"}
result := slices.Collect(FilterKeys(kv, func(k int, v string) bool {
return v == "foo"
}))
fmt.Printf("%v", result)
// Output: [1]
}
func ExampleFilterValues() {
kv := map[int]string{1: "foo", 2: "bar", 3: "baz"}
result := slices.Collect(FilterValues(kv, func(k int, v string) bool {
return v == "foo"
}))
fmt.Printf("%v", result)
// Output: [foo]
}
func ExampleSeqToSeq2() {
result := maps.Collect(SeqToSeq2(slices.Values([]string{"foo", "bar", "baz"})))
fmt.Printf("%v %v %v %v", len(result), result[0], result[1], result[2])
// Output: 3 foo bar baz
}
func ExampleSeq2KeyToSeq() {
result := slices.Collect(Seq2KeyToSeq(maps.All(map[string]int{
"foo": 1,
"bar": 2,
"baz": 3,
})))
sort.Strings(result)
fmt.Printf("%v", result)
// Output: [bar baz foo]
}
func ExampleSeq2ValueToSeq() {
result := slices.Collect(Seq2ValueToSeq(maps.All(map[string]int{
"foo": 1,
"bar": 2,
"baz": 3,
})))
sort.Ints(result)
fmt.Printf("%v", result)
// Output: [1 2 3]
}
+364
View File
@@ -0,0 +1,364 @@
//go:build go1.23
package it
import (
"fmt"
"maps"
"slices"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
func TestKeys(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := slices.Collect(Keys(map[string]int{"foo": 1, "bar": 2}))
is.ElementsMatch(r1, []string{"bar", "foo"})
r2 := slices.Collect(Keys(map[string]int{}))
is.Empty(r2)
r3 := slices.Collect(Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}))
is.ElementsMatch(r3, []string{"bar", "baz", "foo"})
r4 := slices.Collect(Keys[string, int]())
is.Empty(r4)
r5 := slices.Collect(Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 3}))
is.ElementsMatch(r5, []string{"bar", "bar", "foo"})
}
func TestUniqKeys(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := slices.Collect(UniqKeys(map[string]int{"foo": 1, "bar": 2}))
is.ElementsMatch(r1, []string{"bar", "foo"})
r2 := slices.Collect(UniqKeys(map[string]int{}))
is.Empty(r2)
r3 := slices.Collect(UniqKeys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}))
is.ElementsMatch(r3, []string{"bar", "baz", "foo"})
r4 := slices.Collect(UniqKeys[string, int]())
is.Empty(r4)
r5 := slices.Collect(UniqKeys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"foo": 1, "bar": 3}))
is.ElementsMatch(r5, []string{"bar", "foo"})
// check order
r6 := slices.Collect(UniqKeys(map[string]int{"foo": 1}, map[string]int{"bar": 3}))
is.Equal([]string{"foo", "bar"}, r6)
}
func TestValues(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := slices.Collect(Values(map[string]int{"foo": 1, "bar": 2}))
is.ElementsMatch(r1, []int{1, 2})
r2 := slices.Collect(Values(map[string]int{}))
is.Empty(r2)
r3 := slices.Collect(Values(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}))
is.ElementsMatch(r3, []int{1, 2, 3})
r4 := slices.Collect(Values[string, int]())
is.Empty(r4)
r5 := slices.Collect(Values(map[string]int{"foo": 1, "bar": 2}, map[string]int{"foo": 1, "bar": 3}))
is.ElementsMatch(r5, []int{1, 1, 2, 3})
}
func TestUniqValues(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := slices.Collect(UniqValues(map[string]int{"foo": 1, "bar": 2}))
is.ElementsMatch(r1, []int{1, 2})
r2 := slices.Collect(UniqValues(map[string]int{}))
is.Empty(r2)
r3 := slices.Collect(UniqValues(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}))
is.ElementsMatch(r3, []int{1, 2, 3})
r4 := slices.Collect(UniqValues[string, int]())
is.Empty(r4)
r5 := slices.Collect(UniqValues(map[string]int{"foo": 1, "bar": 2}, map[string]int{"foo": 1, "bar": 3}))
is.ElementsMatch(r5, []int{1, 2, 3})
r6 := slices.Collect(UniqValues(map[string]int{"foo": 1, "bar": 1}, map[string]int{"foo": 1, "bar": 3}))
is.ElementsMatch(r6, []int{1, 3})
// check order
r7 := slices.Collect(UniqValues(map[string]int{"foo": 1}, map[string]int{"bar": 3}))
is.Equal([]int{1, 3}, r7)
}
func TestEntries(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := maps.Collect(Entries(map[string]int{"foo": 1, "bar": 2}))
is.Equal(map[string]int{"foo": 1, "bar": 2}, r1)
}
func TestToPairs(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := maps.Collect(ToPairs(map[string]int{"foo": 1, "bar": 2}))
is.Equal(map[string]int{"foo": 1, "bar": 2}, r1)
}
func TestFromEntries(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := FromEntries(maps.All(map[string]int{"foo": 1, "bar": 2}))
is.Len(r1, 2)
is.Equal(1, r1["foo"])
is.Equal(2, r1["bar"])
}
func TestFromPairs(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := FromPairs(maps.All(map[string]int{"baz": 3, "qux": 4}))
is.Len(r1, 2)
is.Equal(3, r1["baz"])
is.Equal(4, r1["qux"])
}
func TestInvert(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := Invert(maps.All(map[string]int{"a": 1, "b": 2}))
r2 := Invert(maps.All(map[string]int{"a": 1, "b": 2, "c": 1}))
is.Equal(map[int]string{1: "a", 2: "b"}, maps.Collect(r1))
is.Len(maps.Collect(r2), 2)
}
func TestAssign(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Assign(values(map[string]int{"a": 1, "b": 2}, map[string]int{"b": 3, "c": 4}))
is.Equal(map[string]int{"a": 1, "b": 3, "c": 4}, result1)
type myMap map[string]int
before := myMap{"": 0, "foobar": 6, "baz": 3}
after := Assign(values(before, before))
is.IsType(myMap{}, after, "type preserved")
}
func TestChunkEntries(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := ChunkEntries(map[string]int{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}, 2)
result2 := ChunkEntries(map[string]int{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}, 3)
result3 := ChunkEntries(map[string]int{}, 2)
result4 := ChunkEntries(map[string]int{"a": 1}, 2)
result5 := ChunkEntries(map[string]int{"a": 1, "b": 2}, 1)
is.Len(slices.Collect(result1), 3)
is.Len(slices.Collect(result2), 2)
is.Empty(slices.Collect(result3))
is.Len(slices.Collect(result4), 1)
is.Len(slices.Collect(result5), 2)
is.PanicsWithValue("it.ChunkEntries: size must be greater than 0", func() {
ChunkEntries(map[string]int{"a": 1}, 0)
})
is.PanicsWithValue("it.ChunkEntries: size must be greater than 0", func() {
ChunkEntries(map[string]int{"a": 1}, -1)
})
type myStruct struct {
Name string
Value int
}
allStructs := []myStruct{{"one", 1}, {"two", 2}, {"three", 3}}
nonempty := ChunkEntries(map[string]myStruct{"a": allStructs[0], "b": allStructs[1], "c": allStructs[2]}, 2)
is.Len(slices.Collect(nonempty), 2)
originalMap := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
result6 := slices.Collect(ChunkEntries(originalMap, 2))
for k := range result6[0] {
result6[0][k] = 10
}
is.Equal(map[string]int{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}, originalMap)
}
func TestMapToSeq(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := MapToSeq(map[int]int{1: 5, 2: 6, 3: 7, 4: 8}, func(k, v int) string {
return fmt.Sprintf("%d_%d", k, v)
})
result2 := MapToSeq(map[int]int{1: 5, 2: 6, 3: 7, 4: 8}, func(k, _ int) string {
return strconv.FormatInt(int64(k), 10)
})
is.ElementsMatch(slices.Collect(result1), []string{"1_5", "2_6", "3_7", "4_8"})
is.ElementsMatch(slices.Collect(result2), []string{"1", "2", "3", "4"})
}
func TestFilterMapToSeq(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := FilterMapToSeq(map[int]int{1: 5, 2: 6, 3: 7, 4: 8}, func(k, v int) (string, bool) {
return fmt.Sprintf("%d_%d", k, v), k%2 == 0
})
result2 := FilterMapToSeq(map[int]int{1: 5, 2: 6, 3: 7, 4: 8}, func(k, _ int) (string, bool) {
return strconv.FormatInt(int64(k), 10), k%2 == 0
})
is.ElementsMatch(slices.Collect(result1), []string{"2_6", "4_8"})
is.ElementsMatch(slices.Collect(result2), []string{"2", "4"})
}
func TestFilterKeys(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := FilterKeys(map[int]string{1: "foo", 2: "bar", 3: "baz"}, func(k int, v string) bool {
return v == "foo"
})
is.Equal([]int{1}, slices.Collect(result1))
result2 := FilterKeys(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(k string, v int) bool {
return false
})
is.Empty(slices.Collect(result2))
}
func TestFilterValues(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := FilterValues(map[int]string{1: "foo", 2: "bar", 3: "baz"}, func(k int, v string) bool {
return v == "foo"
})
is.Equal([]string{"foo"}, slices.Collect(result1))
result2 := FilterValues(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(k string, v int) bool {
return false
})
is.Empty(slices.Collect(result2))
}
func TestSeqToSeq2(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := maps.Collect(SeqToSeq2(values("foo", "bar")))
is.Equal(map[int]string{0: "foo", 1: "bar"}, r1)
r2 := maps.Collect(SeqToSeq2(values[string]()))
is.Empty(r2)
}
func TestSeq2KeyToSeq(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := slices.Collect(Seq2KeyToSeq(maps.All(map[string]int{"foo": 4, "bar": 5})))
is.ElementsMatch([]string{"foo", "bar"}, r1)
r2 := slices.Collect(Seq2KeyToSeq(maps.All(map[string]int{})))
is.Empty(r2)
}
func TestSeq2ValueToSeq(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := slices.Collect(Seq2ValueToSeq(maps.All(map[string]int{"foo": 4, "bar": 5})))
is.ElementsMatch([]int{4, 5}, r1)
r2 := slices.Collect(Seq2ValueToSeq(maps.All(map[string]int{})))
is.Empty(r2)
}
func BenchmarkAssign(b *testing.B) {
counts := []int{32768, 1024, 128, 32, 2}
allDifferentMap := func(b *testing.B, n int) []map[string]int {
b.Helper()
defer b.ResetTimer()
m := make([]map[string]int, 0, n)
for i := 0; i < n; i++ {
m = append(m, map[string]int{
strconv.Itoa(i): i,
strconv.Itoa(i): i,
strconv.Itoa(i): i,
strconv.Itoa(i): i,
strconv.Itoa(i): i,
strconv.Itoa(i): i,
},
)
}
return m
}
allTheSameMap := func(b *testing.B, n int) []map[string]int {
b.Helper()
defer b.ResetTimer()
m := make([]map[string]int, 0, n)
for i := 0; i < n; i++ {
m = append(m, map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 4,
"e": 5,
"f": 6,
},
)
}
return m
}
for _, count := range counts {
differentMap := allDifferentMap(b, count)
sameMap := allTheSameMap(b, count)
b.Run(strconv.Itoa(count), func(b *testing.B) {
testCases := []struct {
name string
in []map[string]int
}{
{"different", differentMap},
{"same", sameMap},
}
for _, tc := range testCases {
b.Run(tc.name, func(b *testing.B) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
result := Assign(values(tc.in...))
_ = result
}
})
}
})
}
}
+142
View File
@@ -0,0 +1,142 @@
//go:build go1.23
package it
import (
"iter"
"github.com/samber/lo"
"github.com/samber/lo/internal/constraints"
)
// Range creates a sequence of numbers (positive and/or negative) with given length.
func Range(elementNum int) iter.Seq[int] {
length := lo.If(elementNum < 0, -elementNum).Else(elementNum)
step := lo.If(elementNum < 0, -1).Else(1)
return func(yield func(int) bool) {
for i, j := 0, 0; i < length; i, j = i+1, j+step {
if !yield(j) {
return
}
}
}
}
// RangeFrom creates a sequence of numbers from start with specified length.
func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum int) iter.Seq[T] {
length := lo.If(elementNum < 0, -elementNum).Else(elementNum)
step := lo.If(elementNum < 0, -1).Else(1)
return func(yield func(T) bool) {
for i, j := 0, start; i < length; i, j = i+1, j+T(step) {
if !yield(j) {
return
}
}
}
}
// RangeWithSteps creates a sequence of numbers (positive and/or negative) progressing from start up to, but not including end.
// step set to zero will return an empty sequence.
func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step T) iter.Seq[T] {
return func(yield func(T) bool) {
if start == end || step == 0 {
return
}
if start < end {
if step < 0 {
return
}
for i := start; i < end; i += step {
if !yield(i) {
return
}
}
}
if step > 0 {
return
}
for i := start; i > end; i += step {
if !yield(i) {
return
}
}
}
}
// Sum sums the values in a collection. If collection is empty 0 is returned.
// Will iterate through the entire sequence.
func Sum[T constraints.Float | constraints.Integer | constraints.Complex](collection iter.Seq[T]) T {
return SumBy(collection, func(item T) T { return item })
}
// SumBy summarizes the values in a collection using the given return value from the iteration function. If collection is empty 0 is returned.
// Will iterate through the entire sequence.
func SumBy[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection iter.Seq[T], transform func(item T) R) R {
var sum R
for item := range collection {
sum += transform(item)
}
return sum
}
// Product gets the product of the values in a collection. If collection is empty 1 is returned.
// Will iterate through the entire sequence.
func Product[T constraints.Float | constraints.Integer | constraints.Complex](collection iter.Seq[T]) T {
return ProductBy(collection, func(item T) T { return item })
}
// ProductBy summarizes the values in a collection using the given return value from the iteration function. If collection is empty 1 is returned.
// Will iterate through the entire sequence.
func ProductBy[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection iter.Seq[T], transform func(item T) R) R {
var product R = 1
for item := range collection {
product *= transform(item)
}
return product
}
// Mean calculates the mean of a collection of numbers.
// Will iterate through the entire sequence.
func Mean[T constraints.Float | constraints.Integer](collection iter.Seq[T]) T {
return MeanBy(collection, func(item T) T { return item })
}
// MeanBy calculates the mean of a collection of numbers using the given return value from the iteration function.
// Will iterate through the entire sequence.
func MeanBy[T any, R constraints.Float | constraints.Integer](collection iter.Seq[T], transform func(item T) R) R {
var sum R
var length R
for item := range collection {
sum += transform(item)
length++
}
if length == 0 {
return 0
}
return sum / length
}
// Mode returns the mode (most frequent value) of a collection.
// If multiple values have the same highest frequency, then multiple values are returned.
// If the collection is empty, then the zero value of T is returned.
// Will iterate through the entire sequence and allocate a map large enough to hold all distinct elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func Mode[T constraints.Integer | constraints.Float](collection iter.Seq[T]) []T {
var mode []T
maxFreq := 0
frequency := make(map[T]int)
for item := range collection {
frequency[item]++
count := frequency[item]
if count > maxFreq {
maxFreq = count
mode = append(mode[:0], item)
} else if count == maxFreq {
mode = append(mode, item)
}
}
return mode
}
+97
View File
@@ -0,0 +1,97 @@
//go:build go1.23
package it
import (
"fmt"
"slices"
)
func ExampleRange() {
result1 := Range(4)
result2 := Range(-4)
result3 := RangeFrom(1, 5)
result4 := RangeFrom(1.0, 5)
result5 := RangeWithSteps(0, 20, 5)
result6 := RangeWithSteps[float32](-1.0, -4.0, -1.0)
result7 := RangeWithSteps(1, 4, -1)
result8 := Range(0)
fmt.Printf("%v\n", slices.Collect(result1))
fmt.Printf("%v\n", slices.Collect(result2))
fmt.Printf("%v\n", slices.Collect(result3))
fmt.Printf("%v\n", slices.Collect(result4))
fmt.Printf("%v\n", slices.Collect(result5))
fmt.Printf("%v\n", slices.Collect(result6))
fmt.Printf("%v\n", slices.Collect(result7))
fmt.Printf("%v\n", slices.Collect(result8))
// Output:
// [0 1 2 3]
// [0 -1 -2 -3]
// [1 2 3 4 5]
// [1 2 3 4 5]
// [0 5 10 15]
// [-1 -2 -3]
// []
// []
}
func ExampleSum() {
ints := slices.Values([]int{1, 2, 3, 4, 5})
sum := Sum(ints)
fmt.Printf("%v", sum)
// Output: 15
}
func ExampleSumBy() {
ints := slices.Values([]string{"foo", "bar"})
result := SumBy(ints, func(item string) int {
return len(item)
})
fmt.Printf("%v", result)
// Output: 6
}
func ExampleProduct() {
ints := slices.Values([]int{1, 2, 3, 4, 5})
result := Product(ints)
fmt.Printf("%v", result)
// Output: 120
}
func ExampleProductBy() {
strs := slices.Values([]string{"foo", "bar"})
result := ProductBy(strs, func(item string) int {
return len(item)
})
fmt.Printf("%v", result)
// Output: 9
}
func ExampleMean() {
ints := slices.Values([]int{1, 2, 3, 4, 5})
result := Mean(ints)
fmt.Printf("%v", result)
// Output: 3
}
func ExampleMeanBy() {
strs := slices.Values([]string{"foo", "bar"})
result := MeanBy(strs, func(item string) int {
return len(item)
})
fmt.Printf("%v", result)
// Output: 3
}
+179
View File
@@ -0,0 +1,179 @@
//go:build go1.23
package it
import (
"slices"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRange(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Range(4)
result2 := Range(-4)
result3 := Range(0)
is.Equal([]int{0, 1, 2, 3}, slices.Collect(result1))
is.Equal([]int{0, -1, -2, -3}, slices.Collect(result2))
is.Empty(slices.Collect(result3))
}
func TestRangeFrom(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := RangeFrom(1, 5)
result2 := RangeFrom(-1, -5)
result3 := RangeFrom(10, 0)
result4 := RangeFrom(2.0, 3)
result5 := RangeFrom(-2.0, -3)
is.Equal([]int{1, 2, 3, 4, 5}, slices.Collect(result1))
is.Equal([]int{-1, -2, -3, -4, -5}, slices.Collect(result2))
is.Empty(slices.Collect(result3))
is.Equal([]float64{2.0, 3.0, 4.0}, slices.Collect(result4))
is.Equal([]float64{-2.0, -3.0, -4.0}, slices.Collect(result5))
}
func TestRangeClose(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := RangeWithSteps(0, 20, 6)
result2 := RangeWithSteps(0, 3, -5)
result3 := RangeWithSteps(1, 1, 0)
result4 := RangeWithSteps(3, 2, 1)
result5 := RangeWithSteps(1.0, 4.0, 2.0)
result6 := RangeWithSteps[float32](-1.0, -4.0, -1.0)
is.Equal([]int{0, 6, 12, 18}, slices.Collect(result1))
is.Empty(slices.Collect(result2))
is.Empty(slices.Collect(result3))
is.Empty(slices.Collect(result4))
is.Equal([]float64{1.0, 3.0}, slices.Collect(result5))
is.Equal([]float32{-1.0, -2.0, -3.0}, slices.Collect(result6))
}
func TestSum(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Sum(values[float32](2.3, 3.3, 4, 5.3))
result2 := Sum(values[int32](2, 3, 4, 5))
result3 := Sum(values[uint32](2, 3, 4, 5))
result4 := Sum(values[uint32]())
result5 := Sum(values[complex128](4_4, 2_2))
is.InEpsilon(14.9, result1, 1e-7)
is.Equal(int32(14), result2)
is.Equal(uint32(14), result3)
is.Equal(uint32(0), result4)
is.Equal(complex128(6_6), result5)
}
func TestSumBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := SumBy(values[float32](2.3, 3.3, 4, 5.3), func(n float32) float32 { return n })
result2 := SumBy(values[int32](2, 3, 4, 5), func(n int32) int32 { return n })
result3 := SumBy(values[uint32](2, 3, 4, 5), func(n uint32) uint32 { return n })
result4 := SumBy(values[uint32](), func(n uint32) uint32 { return n })
result5 := SumBy(values[complex128](4_4, 2_2), func(n complex128) complex128 { return n })
is.InEpsilon(14.9, result1, 1e-7)
is.Equal(int32(14), result2)
is.Equal(uint32(14), result3)
is.Equal(uint32(0), result4)
is.Equal(complex128(6_6), result5)
}
func TestProduct(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Product(values[float32](2.3, 3.3, 4, 5.3))
result2 := Product(values[int32](2, 3, 4, 5))
result3 := Product(values[int32](7, 8, 9, 0))
result4 := Product(values[int32](7, -1, 9, 2))
result5 := Product(values[uint32](2, 3, 4, 5))
result6 := Product(values[uint32]())
result7 := Product(values[complex128](4_4, 2_2))
is.InEpsilon(160.908, result1, 1e-7)
is.Equal(int32(120), result2)
is.Equal(int32(0), result3)
is.Equal(int32(-126), result4)
is.Equal(uint32(120), result5)
is.Equal(uint32(1), result6)
is.Equal(complex128(96_8), result7)
}
func TestProductBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := ProductBy(values[float32](2.3, 3.3, 4, 5.3), func(n float32) float32 { return n })
result2 := ProductBy(values[int32](2, 3, 4, 5), func(n int32) int32 { return n })
result3 := ProductBy(values[int32](7, 8, 9, 0), func(n int32) int32 { return n })
result4 := ProductBy(values[int32](7, -1, 9, 2), func(n int32) int32 { return n })
result5 := ProductBy(values[uint32](2, 3, 4, 5), func(n uint32) uint32 { return n })
result6 := ProductBy(values[uint32](), func(n uint32) uint32 { return n })
result7 := ProductBy(values[complex128](4_4, 2_2), func(n complex128) complex128 { return n })
is.InEpsilon(160.908, result1, 1e-7)
is.Equal(int32(120), result2)
is.Equal(int32(0), result3)
is.Equal(int32(-126), result4)
is.Equal(uint32(120), result5)
is.Equal(uint32(1), result6)
is.Equal(complex128(96_8), result7)
}
func TestMean(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Mean(values[float32](2.3, 3.3, 4, 5.3))
result2 := Mean(values[int32](2, 3, 4, 5))
result3 := Mean(values[uint32](2, 3, 4, 5))
result4 := Mean(values[uint32]())
is.InEpsilon(3.725, result1, 1e-7)
is.Equal(int32(3), result2)
is.Equal(uint32(3), result3)
is.Equal(uint32(0), result4)
}
func TestMeanBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := MeanBy(values[float32](2.3, 3.3, 4, 5.3), func(n float32) float32 { return n })
result2 := MeanBy(values[int32](2, 3, 4, 5), func(n int32) int32 { return n })
result3 := MeanBy(values[uint32](2, 3, 4, 5), func(n uint32) uint32 { return n })
result4 := MeanBy(values[uint32](), func(n uint32) uint32 { return n })
is.InEpsilon(3.725, result1, 1e-7)
is.Equal(int32(3), result2)
is.Equal(uint32(3), result3)
is.Equal(uint32(0), result4)
}
func TestMode(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Mode(values[float32](2.3, 3.3, 3.3, 5.3))
result2 := Mode(values[int32](2, 2, 3, 4))
result3 := Mode(values[uint32](2, 2, 3, 3))
result4 := Mode(values[uint32]())
result5 := Mode(values(1, 2, 3, 4, 5, 6, 7, 8, 9))
is.Equal([]float32{3.3}, result1)
is.Equal([]int32{2}, result2)
is.Equal([]uint32{2, 3}, result3)
is.Empty(result4)
is.Equal([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}, result5)
}
+904
View File
@@ -0,0 +1,904 @@
//go:build go1.23
package it
import (
"iter"
"slices"
"github.com/samber/lo"
"github.com/samber/lo/internal/constraints"
"github.com/samber/lo/mutable"
)
// Length returns the length of collection.
// Will iterate through the entire sequence.
func Length[T any](collection iter.Seq[T]) int {
var count int
for range collection {
count++
}
return count
}
// Drain consumes an entire sequence.
func Drain[T any](collection iter.Seq[T]) {
for range collection { //nolint:revive
}
}
// Filter iterates over elements of collection, returning a sequence of all elements predicate returns true for.
func Filter[T any, I ~func(func(T) bool)](collection I, predicate func(item T) bool) I {
return FilterI(collection, func(item T, _ int) bool { return predicate(item) })
}
// FilterI iterates over elements of collection, returning a sequence of all elements predicate returns true for.
func FilterI[T any, I ~func(func(T) bool)](collection I, predicate func(item T, index int) bool) I {
return func(yield func(T) bool) {
var i int
for item := range collection {
if predicate(item, i) && !yield(item) {
return
}
i++
}
}
}
// Map manipulates a sequence and transforms it to a sequence of another type.
func Map[T, R any](collection iter.Seq[T], transform func(item T) R) iter.Seq[R] {
return MapI(collection, func(item T, _ int) R { return transform(item) })
}
// MapI manipulates a sequence and transforms it to a sequence of another type.
func MapI[T, R any](collection iter.Seq[T], transform func(item T, index int) R) iter.Seq[R] {
return func(yield func(R) bool) {
var i int
for item := range collection {
if !yield(transform(item, i)) {
return
}
i++
}
}
}
// UniqMap manipulates a sequence and transforms it to a sequence of another type with unique values.
// Will allocate a map large enough to hold all distinct transformed elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func UniqMap[T any, R comparable](collection iter.Seq[T], transform func(item T) R) iter.Seq[R] {
return Uniq(Map(collection, transform))
}
// UniqMapI manipulates a sequence and transforms it to a sequence of another type with unique values.
// Will allocate a map large enough to hold all distinct transformed elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func UniqMapI[T any, R comparable](collection iter.Seq[T], transform func(item T, index int) R) iter.Seq[R] {
return Uniq(MapI(collection, transform))
}
// FilterMap returns a sequence obtained after both filtering and mapping using the given callback function.
// The callback function should return two values:
// - the result of the mapping operation and
// - whether the result element should be included or not.
func FilterMap[T, R any](collection iter.Seq[T], callback func(item T) (R, bool)) iter.Seq[R] {
return FilterMapI(collection, func(item T, _ int) (R, bool) { return callback(item) })
}
// FilterMapI returns a sequence obtained after both filtering and mapping using the given callback function.
// The callback function should return two values:
// - the result of the mapping operation and
// - whether the result element should be included or not.
func FilterMapI[T, R any](collection iter.Seq[T], callback func(item T, index int) (R, bool)) iter.Seq[R] {
return func(yield func(R) bool) {
var i int
for item := range collection {
if r, ok := callback(item, i); ok && !yield(r) {
return
}
i++
}
}
}
// FlatMap manipulates a sequence and transforms and flattens it to a sequence of another type.
// The transform function can either return a sequence or a `nil`, and in the `nil` case
// no value is yielded.
func FlatMap[T, R any](collection iter.Seq[T], transform func(item T) iter.Seq[R]) iter.Seq[R] {
return FlatMapI(collection, func(item T, _ int) iter.Seq[R] { return transform(item) })
}
// FlatMapI manipulates a sequence and transforms and flattens it to a sequence of another type.
// The transform function can either return a sequence or a `nil`, and in the `nil` case
// no value is yielded.
func FlatMapI[T, R any](collection iter.Seq[T], transform func(item T, index int) iter.Seq[R]) iter.Seq[R] {
return func(yield func(R) bool) {
var i int
for item := range collection {
for r := range transform(item, i) {
if !yield(r) {
return
}
}
i++
}
}
}
// Reduce reduces collection to a value which is the accumulated result of running each element in collection
// through accumulator, where each successive invocation is supplied the return value of the previous.
// Will iterate through the entire sequence.
func Reduce[T, R any](collection iter.Seq[T], accumulator func(agg R, item T) R, initial R) R {
return ReduceI(collection, func(agg R, item T, _ int) R { return accumulator(agg, item) }, initial)
}
// ReduceI reduces collection to a value which is the accumulated result of running each element in collection
// through accumulator, where each successive invocation is supplied the return value of the previous.
// Will iterate through the entire sequence.
func ReduceI[T, R any](collection iter.Seq[T], accumulator func(agg R, item T, index int) R, initial R) R {
var i int
for item := range collection {
initial = accumulator(initial, item, i)
i++
}
return initial
}
// ReduceLast is like Reduce except that it iterates over elements of collection in reverse.
// Will iterate through the entire sequence and allocate a slice large enough to hold all elements.
// Long input sequences can cause excessive memory usage.
func ReduceLast[T, R any](collection iter.Seq[T], accumulator func(agg R, item T) R, initial R) R {
return Reduce(Reverse(collection), accumulator, initial)
}
// ReduceLastI is like Reduce except that it iterates over elements of collection in reverse.
// Will iterate through the entire sequence and allocate a slice large enough to hold all elements.
// Long input sequences can cause excessive memory usage.
func ReduceLastI[T, R any](collection iter.Seq[T], accumulator func(agg R, item T, index int) R, initial R) R {
return ReduceI(Reverse(collection), accumulator, initial)
}
// ForEach iterates over elements of collection and invokes transform for each element.
// Will iterate through the entire sequence.
func ForEach[T any](collection iter.Seq[T], transform func(item T)) {
ForEachI(collection, func(item T, _ int) { transform(item) })
}
// ForEachI iterates over elements of collection and invokes transform for each element.
// Will iterate through the entire sequence.
func ForEachI[T any](collection iter.Seq[T], transform func(item T, index int)) {
var i int
for item := range collection {
transform(item, i)
i++
}
}
// ForEachWhile iterates over elements of collection and invokes predicate for each element
// collection return value decide to continue or break, like do while().
// Will iterate through the entire sequence.
func ForEachWhile[T any](collection iter.Seq[T], predicate func(item T) bool) {
ForEachWhileI(collection, func(item T, _ int) bool { return predicate(item) })
}
// ForEachWhileI iterates over elements of collection and invokes predicate for each element
// collection return value decide to continue or break, like do while().
// Will iterate through the entire sequence.
func ForEachWhileI[T any](collection iter.Seq[T], predicate func(item T, index int) bool) {
var i int
for item := range collection {
if !predicate(item, i) {
return
}
i++
}
}
// Times invokes transform n times and returns a sequence of results.
// The transform is invoked with index as argument.
func Times[T any](count int, transform func(index int) T) iter.Seq[T] {
return func(yield func(T) bool) {
for i := 0; i < count; i++ {
if !yield(transform(i)) {
return
}
}
}
}
// Uniq returns a duplicate-free version of a sequence, in which only the first occurrence of each element is kept.
// The order of result values is determined by the order they occur in the sequence.
// Will allocate a map large enough to hold all distinct elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func Uniq[T comparable, I ~func(func(T) bool)](collection I) I {
return UniqBy(collection, func(item T) T { return item })
}
// UniqBy returns a duplicate-free version of a sequence, in which only the first occurrence of each element is kept.
// The order of result values is determined by the order they occur in the sequence. A transform function is
// invoked for each element in the sequence to generate the criterion by which uniqueness is computed.
// Will allocate a map large enough to hold all distinct transformed elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func UniqBy[T any, U comparable, I ~func(func(T) bool)](collection I, transform func(item T) U) I {
return func(yield func(T) bool) {
seen := make(map[U]struct{})
for item := range collection {
key := transform(item)
if _, ok := seen[key]; !ok {
if !yield(item) {
return
}
seen[key] = struct{}{}
}
}
}
}
// GroupBy returns an object composed of keys generated from the results of running each element of collection through transform.
// Will iterate through the entire sequence.
func GroupBy[T any, U comparable](collection iter.Seq[T], transform func(item T) U) map[U][]T {
return GroupByMap(collection, func(item T) (U, T) { return transform(item), item })
}
// GroupByMap returns an object composed of keys generated from the results of running each element of collection through transform.
// Will iterate through the entire sequence.
func GroupByMap[T any, K comparable, V any](collection iter.Seq[T], transform func(item T) (K, V)) map[K][]V {
result := make(map[K][]V)
for item := range collection {
k, v := transform(item)
result[k] = append(result[k], v)
}
return result
}
// Chunk returns a sequence of elements split into groups of length size. If the sequence can't be split evenly,
// the final chunk will be the remaining elements.
func Chunk[T any](collection iter.Seq[T], size int) iter.Seq[[]T] {
if size <= 0 {
panic("it.Chunk: size must be greater than 0")
}
return func(yield func([]T) bool) {
var newSlice []T
for item := range collection {
if newSlice == nil {
newSlice = make([]T, 0, size)
}
newSlice = append(newSlice, item)
if len(newSlice) == size {
if !yield(newSlice) {
return
}
newSlice = nil
}
}
if newSlice != nil {
yield(newSlice)
}
}
}
// PartitionBy returns a sequence of elements split into groups. The order of grouped values is
// determined by the order they occur in collection. The grouping is generated from the results
// of running each element of collection through transform.
// Will allocate a map large enough to hold all distinct transformed elements.
// Long heterogeneous input sequences can cause excessive memory usage.
func PartitionBy[T any, K comparable](collection iter.Seq[T], transform func(item T) K) [][]T {
var result [][]T
seen := map[K]int{}
for item := range collection {
key := transform(item)
resultIndex, ok := seen[key]
if !ok {
resultIndex = len(result)
seen[key] = resultIndex
result = append(result, []T{})
}
result[resultIndex] = append(result[resultIndex], item)
}
return result
}
// Flatten returns a sequence a single level deep.
func Flatten[T any, I ~func(func(T) bool)](collection []I) I {
return func(yield func(T) bool) {
for _, item := range collection {
for item := range item {
if !yield(item) {
return
}
}
}
}
}
// Interleave round-robin alternating input sequences and sequentially appending value at index into result.
// Will allocate a slice the size of collections.
func Interleave[T any](collections ...iter.Seq[T]) iter.Seq[T] {
return func(yield func(T) bool) {
next := make([]func() (T, bool), len(collections))
for i, c := range collections {
var stop func()
next[i], stop = iter.Pull(c)
defer stop()
}
var done int
for done < len(next) {
done = 0
for i, n := range next {
if n == nil {
done++
} else if t, ok := n(); !ok {
next[i] = nil
done++
} else if !yield(t) {
return
}
}
}
}
}
// Shuffle returns a sequence of shuffled values. Uses the Fisher-Yates shuffle algorithm.
// Will iterate through the entire sequence and allocate a slice large enough to hold all elements.
// Long input sequences can cause excessive memory usage.
func Shuffle[T any, I ~func(func(T) bool)](collection I) I {
slice := slices.Collect(iter.Seq[T](collection))
mutable.Shuffle(slice)
return I(slices.Values(slice))
}
// Reverse reverses a sequence so that the first element becomes the last, the second element becomes the second to last, and so on.
// Will iterate through the entire sequence and allocate a slice large enough to hold all elements.
// Long input sequences can cause excessive memory usage.
func Reverse[T any, I ~func(func(T) bool)](collection I) I {
slice := slices.Collect(iter.Seq[T](collection))
mutable.Reverse(slice)
return I(slices.Values(slice))
}
// Fill replaces elements of a sequence with `initial` value.
func Fill[T lo.Clonable[T], I ~func(func(T) bool)](collection I, initial T) I {
return func(yield func(T) bool) {
for range collection {
if !yield(initial.Clone()) {
return
}
}
}
}
// Repeat builds a sequence with N copies of initial value.
func Repeat[T lo.Clonable[T]](count int, initial T) iter.Seq[T] {
return RepeatBy(count, func(int) T { return initial.Clone() })
}
// RepeatBy builds a sequence with values returned by N calls of transform.
func RepeatBy[T any](count int, transform func(index int) T) iter.Seq[T] {
return func(yield func(T) bool) {
for i := range count {
if !yield(transform(i)) {
return
}
}
}
}
// KeyBy transforms a sequence to a map based on a pivot transform function.
// Will iterate through the entire sequence.
func KeyBy[K comparable, V any](collection iter.Seq[V], transform func(item V) K) map[K]V {
result := make(map[K]V)
for item := range collection {
k := transform(item)
result[k] = item
}
return result
}
// Associate returns a map containing key-value pairs provided by transform function applied to elements of the given sequence.
// If any of two pairs have the same key the last one gets added to the map.
// The order of keys in returned map is not specified and is not guaranteed to be the same from the original sequence.
// Will iterate through the entire sequence.
func Associate[T any, K comparable, V any](collection iter.Seq[T], transform func(item T) (K, V)) map[K]V {
result := make(map[K]V)
for item := range collection {
k, v := transform(item)
result[k] = v
}
return result
}
// SeqToMap returns a map containing key-value pairs provided by transform function applied to elements of the given sequence.
// If any of two pairs have the same key the last one gets added to the map.
// The order of keys in returned map is not specified and is not guaranteed to be the same from the original sequence.
// Alias of Associate().
// Will iterate through the entire sequence.
func SeqToMap[T any, K comparable, V any](collection iter.Seq[T], transform func(item T) (K, V)) map[K]V {
return Associate(collection, transform)
}
// FilterSeqToMap returns a map containing key-value pairs provided by transform function applied to elements of the given sequence.
// If any of two pairs have the same key the last one gets added to the map.
// The order of keys in returned map is not specified and is not guaranteed to be the same from the original sequence.
// The third return value of the transform function is a boolean that indicates whether the key-value pair should be included in the map.
// Will iterate through the entire sequence.
func FilterSeqToMap[T any, K comparable, V any](collection iter.Seq[T], transform func(item T) (K, V, bool)) map[K]V {
result := make(map[K]V)
for item := range collection {
if k, v, ok := transform(item); ok {
result[k] = v
}
}
return result
}
// Keyify returns a map with each unique element of the sequence as a key.
// Will iterate through the entire sequence.
func Keyify[T comparable](collection iter.Seq[T]) map[T]struct{} {
result := make(map[T]struct{})
for item := range collection {
result[item] = struct{}{}
}
return result
}
// Drop drops n elements from the beginning of a sequence.
func Drop[T any, I ~func(func(T) bool)](collection I, n int) I {
if n < 0 {
panic("it.Drop: n must not be negative")
}
if n == 0 {
return collection
}
return FilterI(collection, func(item T, index int) bool { return index >= n })
}
// DropLast drops n elements from the end of a sequence.
// Will allocate a slice of length n.
func DropLast[T any, I ~func(func(T) bool)](collection I, n int) I {
if n < 0 {
panic("it.DropLast: n must not be negative")
}
if n == 0 {
return collection
}
return func(yield func(T) bool) {
buf := make([]T, 0, n)
var i int
for item := range collection {
if len(buf) < n {
buf = append(buf, item)
} else {
if !yield(buf[i]) {
return
}
buf[i] = item
i = (i + 1) % n
}
}
}
}
// DropWhile drops elements from the beginning of a sequence while the predicate returns true.
func DropWhile[T any, I ~func(func(T) bool)](collection I, predicate func(item T) bool) I {
return func(yield func(T) bool) {
dropping := true
for item := range collection {
if dropping && !predicate(item) {
dropping = false
}
if !dropping && !yield(item) {
return
}
}
}
}
// DropLastWhile drops elements from the end of a sequence while the predicate returns true.
// Will allocate a slice large enough to hold the longest sequence of matching elements.
// Long input sequences of consecutive matches can cause excessive memory usage.
func DropLastWhile[T any, I ~func(func(T) bool)](collection I, predicate func(item T) bool) I {
return func(yield func(T) bool) {
var buf []T
for item := range collection {
if predicate(item) {
buf = append(buf, item)
continue
}
if len(buf) > 0 {
for _, item := range buf {
if !yield(item) {
return
}
}
buf = buf[:0]
}
if !yield(item) {
return
}
}
}
}
// DropByIndex drops elements from a sequence by the index.
// Will allocate a map large enough to hold all distinct indexes.
func DropByIndex[T any, I ~func(func(T) bool)](collection I, indexes ...int) I {
set := lo.Keyify(indexes)
return RejectI(collection, func(_ T, index int) bool { return lo.HasKey(set, index) })
}
// Reject is the opposite of Filter, this method returns the elements of collection that predicate does not return true for.
func Reject[T any, I ~func(func(T) bool)](collection I, predicate func(item T) bool) I {
return RejectI(collection, func(item T, _ int) bool { return predicate(item) })
}
// RejectI is the opposite of Filter, this method returns the elements of collection that predicate does not return true for.
func RejectI[T any, I ~func(func(T) bool)](collection I, predicate func(item T, index int) bool) I {
return func(yield func(T) bool) {
var i int
for item := range collection {
if !predicate(item, i) && !yield(item) {
return
}
i++
}
}
}
// RejectMap is the opposite of FilterMap, this method returns a sequence obtained after both filtering and mapping using the given callback function.
// The callback function should return two values:
// - the result of the mapping operation and
// - whether the result element should be included or not.
func RejectMap[T, R any](collection iter.Seq[T], callback func(item T) (R, bool)) iter.Seq[R] {
return RejectMapI(collection, func(item T, _ int) (R, bool) { return callback(item) })
}
// RejectMapI is the opposite of FilterMap, this method returns a sequence obtained after both filtering and mapping using the given callback function.
// The callback function should return two values:
// - the result of the mapping operation and
// - whether the result element should be included or not.
func RejectMapI[T, R any](collection iter.Seq[T], callback func(item T, index int) (R, bool)) iter.Seq[R] {
return func(yield func(R) bool) {
var i int
for item := range collection {
if r, ok := callback(item, i); !ok && !yield(r) {
return
}
i++
}
}
}
// Count counts the number of elements in the collection that equal value.
// Will iterate through the entire sequence.
func Count[T comparable](collection iter.Seq[T], value T) int {
return CountBy(collection, func(item T) bool { return item == value })
}
// CountBy counts the number of elements in the collection for which predicate is true.
// Will iterate through the entire sequence.
func CountBy[T any](collection iter.Seq[T], predicate func(item T) bool) int {
var count int
for range Filter(collection, predicate) {
count++
}
return count
}
// CountValues counts the number of each element in the collection.
// Will iterate through the entire sequence.
func CountValues[T comparable](collection iter.Seq[T]) map[T]int {
return CountValuesBy(collection, func(item T) T { return item })
}
// CountValuesBy counts the number of each element returned from transform function.
// Is equivalent to chaining Map and CountValues.
// Will iterate through the entire sequence.
func CountValuesBy[T any, U comparable](collection iter.Seq[T], transform func(item T) U) map[U]int {
result := make(map[U]int)
for item := range collection {
result[transform(item)]++
}
return result
}
// Subset returns a subset of a sequence from `offset` up to `length` elements.
// Will iterate at most offset+length times.
func Subset[T any, I ~func(func(T) bool)](collection I, offset, length int) I {
if offset < 0 {
panic("it.Subset: offset must not be negative")
}
if length < 0 {
panic("it.Subset: length must not be negative")
}
return Slice(collection, offset, offset+length)
}
// Slice returns a subset of a sequence from `start` up to, but not including `end`.
// Will iterate at most end times.
func Slice[T any, I ~func(func(T) bool)](collection I, start, end int) I {
if start < 0 {
start = 0
}
if end < 0 {
end = 0
}
return func(yield func(T) bool) {
var i int
for item := range collection {
if i >= start && (i >= end || !yield(item)) {
return
}
i++
}
}
}
// Replace returns a sequence with the first n non-overlapping instances of old replaced by new.
func Replace[T comparable, I ~func(func(T) bool)](collection I, old, nEw T, n int) I {
return I(Map(iter.Seq[T](collection), func(item T) T {
if n != 0 && item == old {
n--
return nEw
}
return item
}))
}
// ReplaceAll returns a sequence with all non-overlapping instances of old replaced by new.
func ReplaceAll[T comparable, I ~func(func(T) bool)](collection I, old, nEw T) I {
return Replace(collection, old, nEw, -1)
}
// Compact returns a sequence of all non-zero elements.
func Compact[T comparable, I ~func(func(T) bool)](collection I) I {
return Filter(collection, lo.IsNotEmpty)
}
// IsSorted checks if a sequence is sorted.
// Will iterate through the entire sequence.
func IsSorted[T constraints.Ordered](collection iter.Seq[T]) bool {
return IsSortedBy(collection, func(item T) T { return item })
}
// IsSortedBy checks if a sequence is sorted by transform.
// Will iterate through the entire sequence.
func IsSortedBy[T any, K constraints.Ordered](collection iter.Seq[T], transform func(item T) K) bool {
first := true
var prev K
for item := range collection {
key := transform(item)
if first {
first = false
} else if prev > key {
return false
}
prev = key
}
return true
}
// Splice inserts multiple elements at index i. The helper is protected against overflow errors.
func Splice[T any, I ~func(func(T) bool)](collection I, index int, elements ...T) I {
if index < 0 {
panic("it.Splice: index must not be negative")
}
if len(elements) == 0 {
return collection
}
return func(yield func(T) bool) {
var i int
for item := range collection {
if i == index {
for _, element := range elements {
if !yield(element) {
return
}
}
}
if !yield(item) {
return
}
i++
}
if i <= index {
for _, element := range elements {
if !yield(element) {
return
}
}
}
}
}
// CutPrefix returns collection without the provided leading prefix
// and reports whether it found the prefix.
// If collection doesn't start with prefix, CutPrefix returns collection, false.
// If prefix is empty, CutPrefix returns collection, true.
// Will iterate at most the size of separator before returning.
func CutPrefix[T comparable, I ~func(func(T) bool)](collection I, separator []T) (after I, found bool) { //nolint:gocyclo
if len(separator) == 0 {
return collection, true
}
next, stop := iter.Pull(iter.Seq[T](collection))
for i := range separator {
item, ok := next()
if !ok {
return func(yield func(T) bool) {
defer stop()
for j := 0; j < i; j++ {
if !yield(separator[j]) {
return
}
}
}, false
}
if item != separator[i] {
return func(yield func(T) bool) {
defer stop()
for j := 0; j < i; j++ {
if !yield(separator[j]) {
return
}
}
if ok && !yield(item) {
return
}
for {
if item, ok := next(); !ok || !yield(item) {
return
}
}
}, false
}
}
return func(yield func(T) bool) {
defer stop()
for {
if item, ok := next(); !ok || !yield(item) {
return
}
}
}, true
}
// CutSuffix returns collection without the provided ending suffix and reports
// whether it found the suffix. If collection doesn't end with suffix, CutSuffix returns collection, false.
// If suffix is empty, CutSuffix returns collection, true.
// Will iterate through the entire sequence and allocate a slice large enough to hold all elements.
// Long input sequences can cause excessive memory usage.
func CutSuffix[T comparable, I ~func(func(T) bool)](collection I, separator []T) (before I, found bool) {
slice := slices.Collect(iter.Seq[T](collection))
result, ok := lo.CutSuffix(slice, separator)
return I(slices.Values(result)), ok
}
// Trim removes all the leading and trailing cutset from the collection.
// Will allocate a map large enough to hold all distinct cutset elements.
func Trim[T comparable, I ~func(func(T) bool)](collection I, cutset ...T) I {
predicate := lo.Partial(lo.HasKey, lo.Keyify(cutset))
return DropWhile(DropLastWhile(collection, predicate), predicate)
}
// TrimFirst removes all the leading cutset from the collection.
// Will allocate a map large enough to hold all distinct cutset elements.
func TrimFirst[T comparable, I ~func(func(T) bool)](collection I, cutset ...T) I {
return DropWhile(collection, lo.Partial(lo.HasKey, lo.Keyify(cutset)))
}
// TrimPrefix removes all the leading prefix from the collection.
func TrimPrefix[T comparable, I ~func(func(T) bool)](collection I, prefix []T) I {
n := len(prefix)
if n == 0 {
return collection
}
return func(yield func(T) bool) {
var i int
for item := range collection {
if i < 0 {
if !yield(item) {
return
}
continue
}
if item == prefix[i] {
i = (i + 1) % n
continue
}
for j := 0; j < i; j++ {
if !yield(prefix[j]) {
return
}
}
if !yield(item) {
return
}
i = -1
}
for j := 0; j < i; j++ {
if !yield(prefix[j]) {
return
}
}
}
}
// TrimLast removes all the trailing cutset from the collection.
// Will allocate a map large enough to hold all distinct cutset elements.
func TrimLast[T comparable, I ~func(func(T) bool)](collection I, cutset ...T) I {
return DropLastWhile(collection, lo.Partial(lo.HasKey, lo.Keyify(cutset)))
}
// TrimSuffix removes all the trailing suffix from the collection.
func TrimSuffix[T comparable, I ~func(func(T) bool)](collection I, suffix []T) I {
n := len(suffix)
if n == 0 {
return collection
}
return func(yield func(T) bool) {
var i int
for item := range collection {
if item == suffix[i%n] {
i++
} else {
for j := 0; j < i; j++ {
if !yield(suffix[j%n]) {
return
}
}
i = 0
if item == suffix[i] {
i++
} else if !yield(item) {
return
}
}
}
if i%n != 0 {
for j := 0; j < i; j++ {
if !yield(suffix[j%n]) {
return
}
}
}
}
}
+160
View File
@@ -0,0 +1,160 @@
//go:build go1.23
package it
import (
"fmt"
"iter"
"math/rand/v2"
"strconv"
"testing"
)
var lengths = []int{10, 100, 1000}
func BenchmarkChunk(b *testing.B) {
for _, n := range lengths {
strs := genStrings(n)
b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Chunk(strs, 5)
}
})
}
for _, n := range lengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Chunk(ints, 5)
}
})
}
}
func genStrings(n int) iter.Seq[string] {
return func(yield func(string) bool) {
for range n {
if !yield(strconv.Itoa(rand.IntN(100_000))) {
break
}
}
}
}
func genInts(n int) iter.Seq[int] {
return func(yield func(int) bool) {
for range n {
if !yield(rand.IntN(100_000)) {
break
}
}
}
}
func BenchmarkFlatten(b *testing.B) {
for _, n := range lengths {
ints := make([]iter.Seq[int], 0, n)
for i := 0; i < n; i++ {
ints = append(ints, genInts(n))
}
b.Run(fmt.Sprintf("ints_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Flatten(ints)
}
})
}
for _, n := range lengths {
strs := make([]iter.Seq[string], 0, n)
for i := 0; i < n; i++ {
strs = append(strs, genStrings(n))
}
b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Flatten(strs)
}
})
}
}
func BenchmarkDrop(b *testing.B) {
for _, n := range lengths {
strs := genStrings(n)
b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Drop(strs, n/4)
}
})
}
for _, n := range lengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Drop(ints, n/4)
}
})
}
}
func BenchmarkDropWhile(b *testing.B) {
for _, n := range lengths {
strs := genStrings(n)
b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = DropWhile(strs, func(v string) bool { return len(v) < 4 })
}
})
}
for _, n := range lengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = DropWhile(ints, func(v int) bool { return i < 10_000 })
}
})
}
}
func BenchmarkDropByIndex(b *testing.B) {
for _, n := range lengths {
strs := genStrings(n)
b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = DropByIndex(strs, n/4)
}
})
}
for _, n := range lengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = DropByIndex(ints, n/4)
}
})
}
}
func BenchmarkReplace(b *testing.B) {
lengths := []int{1_000, 10_000, 100_000}
for _, n := range lengths {
strs := genStrings(n)
b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Replace(strs, "321321", "123123", 10)
}
})
}
for _, n := range lengths {
ints := genInts(n)
b.Run(fmt.Sprintf("ints%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Replace(ints, 321321, 123123, 10)
}
})
}
}
+692
View File
@@ -0,0 +1,692 @@
//go:build go1.23
package it
import (
"fmt"
"iter"
"math"
"slices"
"strconv"
)
func ExampleLength() {
list := slices.Values([]int64{1, 2, 3, 4})
result := Length(list)
fmt.Printf("%v", result)
// Output: 4
}
func ExampleDrain() {
list := slices.Values([]int64{1, 2, 3, 4})
Drain(list)
}
func ExampleFilter() {
list := slices.Values([]int64{1, 2, 3, 4})
result := Filter(list, func(nbr int64) bool {
return nbr%2 == 0
})
fmt.Printf("%v", slices.Collect(result))
// Output: [2 4]
}
func ExampleMap() {
list := slices.Values([]int64{1, 2, 3, 4})
result := Map(list, func(nbr int64) string {
return strconv.FormatInt(nbr*2, 10)
})
fmt.Printf("%v", slices.Collect(result))
// Output: [2 4 6 8]
}
func ExampleUniqMap() {
type User struct {
Name string
Age int
}
users := slices.Values([]User{{Name: "Alex", Age: 10}, {Name: "Alex", Age: 12}, {Name: "Bob", Age: 11}, {Name: "Alice", Age: 20}})
result := UniqMap(users, func(u User) string {
return u.Name
})
fmt.Printf("%v", slices.Collect(result))
// Output: [Alex Bob Alice]
}
func ExampleFilterMap() {
list := slices.Values([]int64{1, 2, 3, 4})
result := FilterMap(list, func(nbr int64) (string, bool) {
return strconv.FormatInt(nbr*2, 10), nbr%2 == 0
})
fmt.Printf("%v", slices.Collect(result))
// Output: [4 8]
}
func ExampleFlatMap() {
list := slices.Values([]int64{1, 2, 3, 4})
result := FlatMap(list, func(nbr int64) iter.Seq[string] {
return slices.Values([]string{
strconv.FormatInt(nbr, 10), // base 10
strconv.FormatInt(nbr, 2), // base 2
})
})
fmt.Printf("%v", slices.Collect(result))
// Output: [1 1 2 10 3 11 4 100]
}
func ExampleReduce() {
list := slices.Values([]int64{1, 2, 3, 4})
result := Reduce(list, func(agg, item int64) int64 {
return agg + item
}, 0)
fmt.Printf("%v", result)
// Output: 10
}
func ExampleReduceLast() {
list := slices.Values([][]int{{0, 1}, {2, 3}, {4, 5}})
result := ReduceLast(list, func(agg, item []int) []int {
return append(agg, item...)
}, []int{})
fmt.Printf("%v", result)
// Output: [4 5 2 3 0 1]
}
func ExampleForEach() {
list := slices.Values([]int64{1, 2, 3, 4})
ForEach(list, func(x int64) {
fmt.Println(x)
})
// Output:
// 1
// 2
// 3
// 4
}
func ExampleForEachWhile() {
list := slices.Values([]int64{1, 2, -math.MaxInt, 4})
ForEachWhile(list, func(x int64) bool {
if x < 0 {
return false
}
fmt.Println(x)
return true
})
// Output:
// 1
// 2
}
func ExampleTimes() {
result := Times(3, func(i int) string {
return strconv.FormatInt(int64(i), 10)
})
fmt.Printf("%v", slices.Collect(result))
// Output: [0 1 2]
}
func ExampleUniq() {
list := slices.Values([]int{1, 2, 2, 1})
result := Uniq(list)
fmt.Printf("%v", slices.Collect(result))
// Output: [1 2]
}
func ExampleUniqBy() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5})
result := UniqBy(list, func(i int) int {
return i % 3
})
fmt.Printf("%v", slices.Collect(result))
// Output: [0 1 2]
}
func ExampleGroupBy() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5})
result := GroupBy(list, func(i int) int {
return i % 3
})
fmt.Printf("%v\n", result[0])
fmt.Printf("%v\n", result[1])
fmt.Printf("%v\n", result[2])
// Output:
// [0 3]
// [1 4]
// [2 5]
}
func ExampleGroupByMap() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5})
result := GroupByMap(list, func(i int) (int, int) {
return i % 3, i * 2
})
fmt.Printf("%v\n", result[0])
fmt.Printf("%v\n", result[1])
fmt.Printf("%v\n", result[2])
// Output:
// [0 6]
// [2 8]
// [4 10]
}
func ExampleChunk() {
list := slices.Values([]int{0, 1, 2, 3, 4})
result := Chunk(list, 2)
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// [0 1]
// [2 3]
// [4]
}
func ExamplePartitionBy() {
list := slices.Values([]int{-2, -1, 0, 1, 2, 3, 4})
result := PartitionBy(list, func(x int) string {
if x < 0 {
return "negative"
} else if x%2 == 0 {
return "even"
}
return "odd"
})
for _, v := range result {
fmt.Printf("%v\n", v)
}
// Output:
// [-2 -1]
// [0 2 4]
// [1 3]
}
func ExampleFlatten() {
list := []iter.Seq[int]{slices.Values([]int{0, 1, 2}), slices.Values([]int{3, 4, 5})}
result := Flatten(list)
fmt.Printf("%v", slices.Collect(result))
// Output: [0 1 2 3 4 5]
}
func ExampleInterleave() {
list1 := []iter.Seq[int]{slices.Values([]int{1, 4, 7}), slices.Values([]int{2, 5, 8}), slices.Values([]int{3, 6, 9})}
list2 := []iter.Seq[int]{slices.Values([]int{1}), slices.Values([]int{2, 5, 8}), slices.Values([]int{3, 6}), slices.Values([]int{4, 7, 9, 10})}
result1 := slices.Collect(Interleave(list1...))
result2 := slices.Collect(Interleave(list2...))
fmt.Printf("%v\n", result1)
fmt.Printf("%v\n", result2)
// Output:
// [1 2 3 4 5 6 7 8 9]
// [1 2 3 4 5 6 7 8 9 10]
}
func ExampleShuffle() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5})
result := slices.Collect(Shuffle(list))
fmt.Printf("%v", result)
}
func ExampleReverse() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5})
result := slices.Collect(Reverse(list))
fmt.Printf("%v", result)
// Output: [5 4 3 2 1 0]
}
func ExampleFill() {
list := slices.Values([]foo{{"a"}, {"a"}})
result := Fill(list, foo{"b"})
fmt.Printf("%v", slices.Collect(result))
// Output: [{b} {b}]
}
func ExampleRepeat() {
result := Repeat(2, foo{"a"})
fmt.Printf("%v", slices.Collect(result))
// Output: [{a} {a}]
}
func ExampleRepeatBy() {
result := RepeatBy(5, func(i int) string {
return strconv.FormatInt(int64(math.Pow(float64(i), 2)), 10)
})
fmt.Printf("%v", slices.Collect(result))
// Output: [0 1 4 9 16]
}
func ExampleKeyBy() {
list := slices.Values([]string{"a", "aa", "aaa"})
result := KeyBy(list, func(str string) int {
return len(str)
})
fmt.Printf("%v", result)
// Output: map[1:a 2:aa 3:aaa]
}
func ExampleSeqToMap() {
list := slices.Values([]string{"a", "aa", "aaa"})
result := SeqToMap(list, func(str string) (string, int) {
return str, len(str)
})
fmt.Printf("%v", result)
// Output: map[a:1 aa:2 aaa:3]
}
func ExampleFilterSeqToMap() {
list := slices.Values([]string{"a", "aa", "aaa"})
result := FilterSeqToMap(list, func(str string) (string, int, bool) {
return str, len(str), len(str) > 1
})
fmt.Printf("%v", result)
// Output: map[aa:2 aaa:3]
}
func ExampleKeyify() {
list := slices.Values([]string{"a", "a", "b", "b", "d"})
set := Keyify(list)
_, ok1 := set["a"]
_, ok2 := set["c"]
fmt.Printf("%v\n", ok1)
fmt.Printf("%v\n", ok2)
fmt.Printf("%v\n", set)
// Output:
// true
// false
// map[a:{} b:{} d:{}]
}
func ExampleDrop() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5})
result := Drop(list, 2)
fmt.Printf("%v", slices.Collect(result))
// Output: [2 3 4 5]
}
func ExampleDropWhile() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5})
result := DropWhile(list, func(val int) bool {
return val < 2
})
fmt.Printf("%v", slices.Collect(result))
// Output: [2 3 4 5]
}
func ExampleDropByIndex() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5})
result := DropByIndex(list, 2)
fmt.Printf("%v", slices.Collect(result))
// Output: [0 1 3 4 5]
}
func ExampleReject() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5})
result := Reject(list, func(x int) bool {
return x%2 == 0
})
fmt.Printf("%v", slices.Collect(result))
// Output: [1 3 5]
}
func ExampleCount() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5, 0, 1, 2, 3})
result := Count(list, 2)
fmt.Printf("%v", result)
// Output: 2
}
func ExampleCountBy() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5, 0, 1, 2, 3})
result := CountBy(list, func(i int) bool {
return i < 4
})
fmt.Printf("%v", result)
// Output: 8
}
func ExampleCountValues() {
result1 := CountValues(slices.Values([]int{}))
result2 := CountValues(slices.Values([]int{1, 2}))
result3 := CountValues(slices.Values([]int{1, 2, 2}))
result4 := CountValues(slices.Values([]string{"foo", "bar", ""}))
result5 := CountValues(slices.Values([]string{"foo", "bar", "bar"}))
fmt.Printf("%v\n", result1)
fmt.Printf("%v\n", result2)
fmt.Printf("%v\n", result3)
fmt.Printf("%v\n", result4)
fmt.Printf("%v\n", result5)
// Output:
// map[]
// map[1:1 2:1]
// map[1:1 2:2]
// map[:1 bar:1 foo:1]
// map[bar:2 foo:1]
}
func ExampleCountValuesBy() {
isEven := func(v int) bool {
return v%2 == 0
}
result1 := CountValuesBy(slices.Values([]int{}), isEven)
result2 := CountValuesBy(slices.Values([]int{1, 2}), isEven)
result3 := CountValuesBy(slices.Values([]int{1, 2, 2}), isEven)
length := func(v string) int {
return len(v)
}
result4 := CountValuesBy(slices.Values([]string{"foo", "bar", ""}), length)
result5 := CountValuesBy(slices.Values([]string{"foo", "bar", "bar"}), length)
fmt.Printf("%v\n", result1)
fmt.Printf("%v\n", result2)
fmt.Printf("%v\n", result3)
fmt.Printf("%v\n", result4)
fmt.Printf("%v\n", result5)
// Output:
// map[]
// map[false:1 true:1]
// map[false:1 true:2]
// map[0:1 3:2]
// map[3:3]
}
func ExampleSubset() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5})
result := Subset(list, 2, 3)
fmt.Printf("%v", slices.Collect(result))
// Output: [2 3 4]
}
func ExampleSlice() {
list := values(0, 1, 2, 3, 4, 5)
result := Slice(list, 1, 4)
fmt.Printf("%v\n", slices.Collect(result))
result = Slice(list, 4, 1)
fmt.Printf("%v\n", slices.Collect(result))
result = Slice(list, 4, 5)
fmt.Printf("%v\n", slices.Collect(result))
// Output:
// [1 2 3]
// []
// [4]
}
func ExampleReplace() {
list := slices.Values([]int{0, 1, 0, 1, 2, 3, 0})
result := Replace(list, 0, 42, 1)
fmt.Printf("%v\n", slices.Collect(result))
result = Replace(list, -1, 42, 1)
fmt.Printf("%v\n", slices.Collect(result))
result = Replace(list, 0, 42, 2)
fmt.Printf("%v\n", slices.Collect(result))
result = Replace(list, 0, 42, -1)
fmt.Printf("%v\n", slices.Collect(result))
// Output:
// [42 1 0 1 2 3 0]
// [0 1 0 1 2 3 0]
// [42 1 42 1 2 3 0]
// [42 1 42 1 2 3 42]
}
func ExampleCompact() {
list := slices.Values([]string{"", "foo", "", "bar", ""})
result := Compact(list)
fmt.Printf("%v", slices.Collect(result))
// Output: [foo bar]
}
func ExampleIsSorted() {
list := slices.Values([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
result := IsSorted(list)
fmt.Printf("%v", result)
// Output: true
}
func ExampleIsSortedBy() {
list := slices.Values([]string{"a", "bb", "ccc"})
result := IsSortedBy(list, func(s string) int {
return len(s)
})
fmt.Printf("%v", result)
// Output: true
}
func ExampleCutPrefix() {
collection := slices.Values([]string{"a", "b", "c", "d", "e", "f", "g"})
// Test with valid prefix
after, found := CutPrefix(collection, []string{"a", "b", "c"})
fmt.Printf("After: %v, Found: %t\n", slices.Collect(after), found)
// Test with prefix not found
after2, found2 := CutPrefix(collection, []string{"b"})
fmt.Printf("After: %v, Found: %t\n", slices.Collect(after2), found2)
// Test with empty prefix
after3, found3 := CutPrefix(collection, []string{})
fmt.Printf("After: %v, Found: %t\n", slices.Collect(after3), found3)
// Output:
// After: [d e f g], Found: true
// After: [a b c d e f g], Found: false
// After: [a b c d e f g], Found: true
}
func ExampleCutSuffix() {
collection := slices.Values([]string{"a", "b", "c", "d", "e", "f", "g"})
// Test with valid suffix
before, found := CutSuffix(collection, []string{"f", "g"})
fmt.Printf("Before: %v, Found: %t\n", slices.Collect(before), found)
// Test with suffix not found
before2, found2 := CutSuffix(collection, []string{"b"})
fmt.Printf("Before: %v, Found: %t\n", slices.Collect(before2), found2)
// Test with empty suffix
before3, found3 := CutSuffix(collection, []string{})
fmt.Printf("Before: %v, Found: %t\n", slices.Collect(before3), found3)
// Output:
// Before: [a b c d e], Found: true
// Before: [a b c d e f g], Found: false
// Before: [a b c d e f g], Found: true
}
func ExampleTrim() {
collection := slices.Values([]int{0, 1, 2, 0, 3, 0})
// Test with valid cutset
result := Trim(collection, 0)
fmt.Printf("Trim with cutset {0}: %v\n", slices.Collect(result))
// Test with string collection
words := slices.Values([]string{" hello ", "world", " "})
result2 := Trim(words, " ")
fmt.Printf("Trim with string cutset: %v\n", slices.Collect(result2))
// Test with no cutset elements
result3 := Trim(collection, 5)
fmt.Printf("Trim with cutset {5} (not present): %v\n", slices.Collect(result3))
// Output:
// Trim with cutset {0}: [1 2 0 3]
// Trim with string cutset: [ hello world ]
// Trim with cutset {5} (not present): [0 1 2 0 3 0]
}
func ExampleTrimFirst() {
collection := slices.Values([]int{0, 1, 2, 0, 3, 0})
// Test with valid cutset
result := TrimFirst(collection, 0)
fmt.Printf("TrimFirst with cutset {0}: %v\n", slices.Collect(result))
// Test with string collection
words := slices.Values([]string{" hello ", "world", " "})
result2 := TrimFirst(words, " ")
fmt.Printf("TrimFirst with string cutset: %v\n", slices.Collect(result2))
// Test with no cutset elements
result3 := TrimFirst(collection, 5)
fmt.Printf("TrimFirst with cutset {5} (not present): %v\n", slices.Collect(result3))
// Output:
// TrimFirst with cutset {0}: [1 2 0 3 0]
// TrimFirst with string cutset: [ hello world ]
// TrimFirst with cutset {5} (not present): [0 1 2 0 3 0]
}
func ExampleTrimPrefix() {
collection := slices.Values([]int{1, 2, 1, 2, 3})
// Test with valid prefix
result := TrimPrefix(collection, []int{1, 2})
fmt.Printf("TrimPrefix with prefix {1,2}: %v\n", slices.Collect(result))
// Test with string collection
words := slices.Values([]string{"hello", "hello", "world"})
result2 := TrimPrefix(words, []string{"hello"})
fmt.Printf("TrimPrefix with string prefix: %v\n", slices.Collect(result2))
// Test with prefix not present
result3 := TrimPrefix(collection, []int{5, 6})
fmt.Printf("TrimPrefix with prefix {5,6} (not present): %v\n", slices.Collect(result3))
// Output:
// TrimPrefix with prefix {1,2}: [3]
// TrimPrefix with string prefix: [world]
// TrimPrefix with prefix {5,6} (not present): [1 2 1 2 3]
}
func ExampleTrimLast() {
collection := slices.Values([]int{0, 1, 2, 0, 3, 0})
// Test with valid cutset
result := TrimLast(collection, 0)
fmt.Printf("TrimLast with cutset {0}: %v\n", slices.Collect(result))
// Test with string collection
words := slices.Values([]string{" hello ", "world", " "})
result2 := TrimLast(words, " ")
fmt.Printf("TrimLast with string cutset: %v\n", slices.Collect(result2))
// Test with no cutset elements
result3 := TrimLast(collection, 5)
fmt.Printf("TrimLast with cutset {5} (not present): %v\n", slices.Collect(result3))
// Output:
// TrimLast with cutset {0}: [0 1 2 0 3]
// TrimLast with string cutset: [ hello world ]
// TrimLast with cutset {5} (not present): [0 1 2 0 3 0]
}
func ExampleTrimSuffix() {
collection := slices.Values([]int{1, 2, 1, 2, 3})
// Test with valid suffix
result := TrimSuffix(collection, []int{1, 2})
fmt.Printf("TrimSuffix with suffix {1,2}: %v\n", slices.Collect(result))
// Test with string collection
words := slices.Values([]string{"hello", "world", "test"})
result2 := TrimSuffix(words, []string{"test"})
fmt.Printf("TrimSuffix with string suffix: %v\n", slices.Collect(result2))
// Test with suffix not present
result3 := TrimSuffix(collection, []int{5, 6})
fmt.Printf("TrimSuffix with suffix {5,6} (not present): %v\n", slices.Collect(result3))
// Output:
// TrimSuffix with suffix {1,2}: [1 2 1 2 3]
// TrimSuffix with string suffix: [hello world]
// TrimSuffix with suffix {5,6} (not present): [1 2 1 2 3]
}
+1389
View File
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
//go:build go1.23
package it
import "iter"
// ChunkString returns a sequence of strings split into groups of length size. If the string can't be split evenly,
// the final chunk will be the remaining characters.
func ChunkString[T ~string](str T, size int) iter.Seq[T] {
if size <= 0 {
panic("it.ChunkString: size must be greater than 0")
}
return func(yield func(T) bool) {
if len(str) == 0 || size >= len(str) {
yield(str)
return
}
currentLen := 0
currentStart := 0
for i := range str {
if currentLen == size {
if !yield(str[currentStart:i]) {
return
}
currentLen = 0
currentStart = i
}
currentLen++
}
yield(str[currentStart:])
}
}
+25
View File
@@ -0,0 +1,25 @@
//go:build go1.23
package it
import (
"fmt"
"slices"
)
func ExampleChunkString() {
result1 := ChunkString("123456", 2)
result2 := ChunkString("1234567", 2)
result3 := ChunkString("", 2)
result4 := ChunkString("1", 2)
fmt.Printf("%v\n", slices.Collect(result1))
fmt.Printf("%v\n", slices.Collect(result2))
fmt.Printf("%v\n", slices.Collect(result3))
fmt.Printf("%v\n", slices.Collect(result4))
// Output:
// [12 34 56]
// [12 34 56 7]
// []
// [1]
}
+37
View File
@@ -0,0 +1,37 @@
//go:build go1.23
package it
import (
"slices"
"testing"
"github.com/stretchr/testify/assert"
)
func TestChunkString(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := ChunkString("12345", 2)
is.Equal([]string{"12", "34", "5"}, slices.Collect(result1))
result2 := ChunkString("123456", 2)
is.Equal([]string{"12", "34", "56"}, slices.Collect(result2))
result3 := ChunkString("123456", 6)
is.Equal([]string{"123456"}, slices.Collect(result3))
result4 := ChunkString("123456", 10)
is.Equal([]string{"123456"}, slices.Collect(result4))
result5 := ChunkString("", 2)
is.Equal([]string{""}, slices.Collect(result5))
result6 := ChunkString("明1好休2林森", 2)
is.Equal([]string{"明1", "好休", "2林", "森"}, slices.Collect(result6))
is.PanicsWithValue("it.ChunkString: size must be greater than 0", func() {
ChunkString("12345", 0)
})
}
+605
View File
@@ -0,0 +1,605 @@
//go:build go1.23
package it
import (
"iter"
"github.com/samber/lo"
)
// Zip2 creates a sequence of grouped elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func Zip2[A, B any](a iter.Seq[A], b iter.Seq[B]) iter.Seq[lo.Tuple2[A, B]] {
return func(yield func(lo.Tuple2[A, B]) bool) {
var next lo.Tuple2[func() (A, bool), func() (B, bool)]
var stop func()
next.A, stop = iter.Pull(a)
defer stop()
next.B, stop = iter.Pull(b)
defer stop()
for {
var item lo.Tuple2[A, B]
var ok [2]bool
item.A, ok[0] = next.A()
item.B, ok[1] = next.B()
if ok == [2]bool{} {
return
}
yield(item)
}
}
}
// Zip3 creates a sequence of grouped elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func Zip3[A, B, C any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C]) iter.Seq[lo.Tuple3[A, B, C]] {
return func(yield func(lo.Tuple3[A, B, C]) bool) {
var next lo.Tuple3[func() (A, bool), func() (B, bool), func() (C, bool)]
var stop func()
next.A, stop = iter.Pull(a)
defer stop()
next.B, stop = iter.Pull(b)
defer stop()
next.C, stop = iter.Pull(c)
defer stop()
for {
var item lo.Tuple3[A, B, C]
var ok [3]bool
item.A, ok[0] = next.A()
item.B, ok[1] = next.B()
item.C, ok[2] = next.C()
if ok == [3]bool{} {
return
}
yield(item)
}
}
}
// Zip4 creates a sequence of grouped elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func Zip4[A, B, C, D any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D]) iter.Seq[lo.Tuple4[A, B, C, D]] {
return func(yield func(lo.Tuple4[A, B, C, D]) bool) {
var next lo.Tuple4[func() (A, bool), func() (B, bool), func() (C, bool), func() (D, bool)]
var stop func()
next.A, stop = iter.Pull(a)
defer stop()
next.B, stop = iter.Pull(b)
defer stop()
next.C, stop = iter.Pull(c)
defer stop()
next.D, stop = iter.Pull(d)
defer stop()
for {
var item lo.Tuple4[A, B, C, D]
var ok [4]bool
item.A, ok[0] = next.A()
item.B, ok[1] = next.B()
item.C, ok[2] = next.C()
item.D, ok[3] = next.D()
if ok == [4]bool{} {
return
}
yield(item)
}
}
}
// Zip5 creates a sequence of grouped elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func Zip5[A, B, C, D, E any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], e iter.Seq[E]) iter.Seq[lo.Tuple5[A, B, C, D, E]] {
return func(yield func(lo.Tuple5[A, B, C, D, E]) bool) {
var next lo.Tuple5[func() (A, bool), func() (B, bool), func() (C, bool), func() (D, bool), func() (E, bool)]
var stop func()
next.A, stop = iter.Pull(a)
defer stop()
next.B, stop = iter.Pull(b)
defer stop()
next.C, stop = iter.Pull(c)
defer stop()
next.D, stop = iter.Pull(d)
defer stop()
next.E, stop = iter.Pull(e)
defer stop()
for {
var item lo.Tuple5[A, B, C, D, E]
var ok [5]bool
item.A, ok[0] = next.A()
item.B, ok[1] = next.B()
item.C, ok[2] = next.C()
item.D, ok[3] = next.D()
item.E, ok[4] = next.E()
if ok == [5]bool{} {
return
}
yield(item)
}
}
}
// Zip6 creates a sequence of grouped elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func Zip6[A, B, C, D, E, F any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], e iter.Seq[E], f iter.Seq[F]) iter.Seq[lo.Tuple6[A, B, C, D, E, F]] {
return func(yield func(lo.Tuple6[A, B, C, D, E, F]) bool) {
var next lo.Tuple6[func() (A, bool), func() (B, bool), func() (C, bool), func() (D, bool), func() (E, bool), func() (F, bool)]
var stop func()
next.A, stop = iter.Pull(a)
defer stop()
next.B, stop = iter.Pull(b)
defer stop()
next.C, stop = iter.Pull(c)
defer stop()
next.D, stop = iter.Pull(d)
defer stop()
next.E, stop = iter.Pull(e)
defer stop()
next.F, stop = iter.Pull(f)
defer stop()
for {
var item lo.Tuple6[A, B, C, D, E, F]
var ok [6]bool
item.A, ok[0] = next.A()
item.B, ok[1] = next.B()
item.C, ok[2] = next.C()
item.D, ok[3] = next.D()
item.E, ok[4] = next.E()
item.F, ok[5] = next.F()
if ok == [6]bool{} {
return
}
yield(item)
}
}
}
// Zip7 creates a sequence of grouped elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func Zip7[A, B, C, D, E, F, G any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], e iter.Seq[E], f iter.Seq[F], g iter.Seq[G]) iter.Seq[lo.Tuple7[A, B, C, D, E, F, G]] {
return func(yield func(lo.Tuple7[A, B, C, D, E, F, G]) bool) {
var next lo.Tuple7[func() (A, bool), func() (B, bool), func() (C, bool), func() (D, bool), func() (E, bool), func() (F, bool), func() (G, bool)]
var stop func()
next.A, stop = iter.Pull(a)
defer stop()
next.B, stop = iter.Pull(b)
defer stop()
next.C, stop = iter.Pull(c)
defer stop()
next.D, stop = iter.Pull(d)
defer stop()
next.E, stop = iter.Pull(e)
defer stop()
next.F, stop = iter.Pull(f)
defer stop()
next.G, stop = iter.Pull(g)
defer stop()
for {
var item lo.Tuple7[A, B, C, D, E, F, G]
var ok [7]bool
item.A, ok[0] = next.A()
item.B, ok[1] = next.B()
item.C, ok[2] = next.C()
item.D, ok[3] = next.D()
item.E, ok[4] = next.E()
item.F, ok[5] = next.F()
item.G, ok[6] = next.G()
if ok == [7]bool{} {
return
}
yield(item)
}
}
}
// Zip8 creates a sequence of grouped elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func Zip8[A, B, C, D, E, F, G, H any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], e iter.Seq[E], f iter.Seq[F], g iter.Seq[G], h iter.Seq[H]) iter.Seq[lo.Tuple8[A, B, C, D, E, F, G, H]] {
return func(yield func(lo.Tuple8[A, B, C, D, E, F, G, H]) bool) {
var next lo.Tuple8[func() (A, bool), func() (B, bool), func() (C, bool), func() (D, bool), func() (E, bool), func() (F, bool), func() (G, bool), func() (H, bool)]
var stop func()
next.A, stop = iter.Pull(a)
defer stop()
next.B, stop = iter.Pull(b)
defer stop()
next.C, stop = iter.Pull(c)
defer stop()
next.D, stop = iter.Pull(d)
defer stop()
next.E, stop = iter.Pull(e)
defer stop()
next.F, stop = iter.Pull(f)
defer stop()
next.G, stop = iter.Pull(g)
defer stop()
next.H, stop = iter.Pull(h)
defer stop()
for {
var item lo.Tuple8[A, B, C, D, E, F, G, H]
var ok [8]bool
item.A, ok[0] = next.A()
item.B, ok[1] = next.B()
item.C, ok[2] = next.C()
item.D, ok[3] = next.D()
item.E, ok[4] = next.E()
item.F, ok[5] = next.F()
item.G, ok[6] = next.G()
item.H, ok[7] = next.H()
if ok == [8]bool{} {
return
}
yield(item)
}
}
}
// Zip9 creates a sequence of grouped elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func Zip9[A, B, C, D, E, F, G, H, I any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], e iter.Seq[E], f iter.Seq[F], g iter.Seq[G], h iter.Seq[H], i iter.Seq[I]) iter.Seq[lo.Tuple9[A, B, C, D, E, F, G, H, I]] {
return func(yield func(lo.Tuple9[A, B, C, D, E, F, G, H, I]) bool) {
var next lo.Tuple9[func() (A, bool), func() (B, bool), func() (C, bool), func() (D, bool), func() (E, bool), func() (F, bool), func() (G, bool), func() (H, bool), func() (I, bool)]
var stop func()
next.A, stop = iter.Pull(a)
defer stop()
next.B, stop = iter.Pull(b)
defer stop()
next.C, stop = iter.Pull(c)
defer stop()
next.D, stop = iter.Pull(d)
defer stop()
next.E, stop = iter.Pull(e)
defer stop()
next.F, stop = iter.Pull(f)
defer stop()
next.G, stop = iter.Pull(g)
defer stop()
next.H, stop = iter.Pull(h)
defer stop()
next.I, stop = iter.Pull(i)
defer stop()
for {
var item lo.Tuple9[A, B, C, D, E, F, G, H, I]
var ok [9]bool
item.A, ok[0] = next.A()
item.B, ok[1] = next.B()
item.C, ok[2] = next.C()
item.D, ok[3] = next.D()
item.E, ok[4] = next.E()
item.F, ok[5] = next.F()
item.G, ok[6] = next.G()
item.H, ok[7] = next.H()
item.I, ok[8] = next.I()
if ok == [9]bool{} {
return
}
yield(item)
}
}
}
// ZipBy2 creates a sequence of transformed elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func ZipBy2[A, B, Out any](a iter.Seq[A], b iter.Seq[B], transform func(a A, b B) Out) iter.Seq[Out] {
return Map(Zip2(a, b), func(item lo.Tuple2[A, B]) Out {
return transform(item.A, item.B)
})
}
// ZipBy3 creates a sequence of transformed elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func ZipBy3[A, B, C, Out any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], transform func(a A, b B, c C) Out) iter.Seq[Out] {
return Map(Zip3(a, b, c), func(item lo.Tuple3[A, B, C]) Out {
return transform(item.A, item.B, item.C)
})
}
// ZipBy4 creates a sequence of transformed elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func ZipBy4[A, B, C, D, Out any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], transform func(a A, b B, c C, d D) Out) iter.Seq[Out] {
return Map(Zip4(a, b, c, d), func(item lo.Tuple4[A, B, C, D]) Out {
return transform(item.A, item.B, item.C, item.D)
})
}
// ZipBy5 creates a sequence of transformed elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func ZipBy5[A, B, C, D, E, Out any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], e iter.Seq[E], transform func(a A, b B, c C, d D, e E) Out) iter.Seq[Out] {
return Map(Zip5(a, b, c, d, e), func(item lo.Tuple5[A, B, C, D, E]) Out {
return transform(item.A, item.B, item.C, item.D, item.E)
})
}
// ZipBy6 creates a sequence of transformed elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func ZipBy6[A, B, C, D, E, F, Out any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], e iter.Seq[E], f iter.Seq[F], transform func(a A, b B, c C, d D, e E, f F) Out) iter.Seq[Out] {
return Map(Zip6(a, b, c, d, e, f), func(item lo.Tuple6[A, B, C, D, E, F]) Out {
return transform(item.A, item.B, item.C, item.D, item.E, item.F)
})
}
// ZipBy7 creates a sequence of transformed elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func ZipBy7[A, B, C, D, E, F, G, Out any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], e iter.Seq[E], f iter.Seq[F], g iter.Seq[G], transform func(a A, b B, c C, d D, e E, f F, g G) Out) iter.Seq[Out] {
return Map(Zip7(a, b, c, d, e, f, g), func(item lo.Tuple7[A, B, C, D, E, F, G]) Out {
return transform(item.A, item.B, item.C, item.D, item.E, item.F, item.G)
})
}
// ZipBy8 creates a sequence of transformed elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func ZipBy8[A, B, C, D, E, F, G, H, Out any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], e iter.Seq[E], f iter.Seq[F], g iter.Seq[G], h iter.Seq[H], transform func(a A, b B, c C, d D, e E, f F, g G, h H) Out) iter.Seq[Out] {
return Map(Zip8(a, b, c, d, e, f, g, h), func(item lo.Tuple8[A, B, C, D, E, F, G, H]) Out {
return transform(item.A, item.B, item.C, item.D, item.E, item.F, item.G, item.H)
})
}
// ZipBy9 creates a sequence of transformed elements, the first of which contains the first elements
// of the given sequences, the second of which contains the second elements of the given sequences, and so on.
// When collections are different sizes, the Tuple attributes are filled with zero value.
func ZipBy9[A, B, C, D, E, F, G, H, I, Out any](a iter.Seq[A], b iter.Seq[B], c iter.Seq[C], d iter.Seq[D], e iter.Seq[E], f iter.Seq[F], g iter.Seq[G], h iter.Seq[H], i iter.Seq[I], transform func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) iter.Seq[Out] {
return Map(Zip9(a, b, c, d, e, f, g, h, i), func(item lo.Tuple9[A, B, C, D, E, F, G, H, I]) Out {
return transform(item.A, item.B, item.C, item.D, item.E, item.F, item.G, item.H, item.I)
})
}
// CrossJoin2 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments.
// Returns an empty list if a list is empty.
func CrossJoin2[A, B any](listA iter.Seq[A], listB iter.Seq[B]) iter.Seq[lo.Tuple2[A, B]] {
return CrossJoinBy2(listA, listB, lo.T2[A, B])
}
// CrossJoin3 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments.
// Returns an empty list if a list is empty.
func CrossJoin3[A, B, C any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C]) iter.Seq[lo.Tuple3[A, B, C]] {
return CrossJoinBy3(listA, listB, listC, lo.T3[A, B, C])
}
// CrossJoin4 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments.
// Returns an empty list if a list is empty.
func CrossJoin4[A, B, C, D any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D]) iter.Seq[lo.Tuple4[A, B, C, D]] {
return CrossJoinBy4(listA, listB, listC, listD, lo.T4[A, B, C, D])
}
// CrossJoin5 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments.
// Returns an empty list if a list is empty.
func CrossJoin5[A, B, C, D, E any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], listE iter.Seq[E]) iter.Seq[lo.Tuple5[A, B, C, D, E]] {
return CrossJoinBy5(listA, listB, listC, listD, listE, lo.T5[A, B, C, D, E])
}
// CrossJoin6 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments.
// Returns an empty list if a list is empty.
func CrossJoin6[A, B, C, D, E, F any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], listE iter.Seq[E], listF iter.Seq[F]) iter.Seq[lo.Tuple6[A, B, C, D, E, F]] {
return CrossJoinBy6(listA, listB, listC, listD, listE, listF, lo.T6[A, B, C, D, E, F])
}
// CrossJoin7 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments.
// Returns an empty list if a list is empty.
func CrossJoin7[A, B, C, D, E, F, G any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], listE iter.Seq[E], listF iter.Seq[F], listG iter.Seq[G]) iter.Seq[lo.Tuple7[A, B, C, D, E, F, G]] {
return CrossJoinBy7(listA, listB, listC, listD, listE, listF, listG, lo.T7[A, B, C, D, E, F, G])
}
// CrossJoin8 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments.
// Returns an empty list if a list is empty.
func CrossJoin8[A, B, C, D, E, F, G, H any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], listE iter.Seq[E], listF iter.Seq[F], listG iter.Seq[G], listH iter.Seq[H]) iter.Seq[lo.Tuple8[A, B, C, D, E, F, G, H]] {
return CrossJoinBy8(listA, listB, listC, listD, listE, listF, listG, listH, lo.T8[A, B, C, D, E, F, G, H])
}
// CrossJoin9 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments.
// Returns an empty list if a list is empty.
func CrossJoin9[A, B, C, D, E, F, G, H, I any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], listE iter.Seq[E], listF iter.Seq[F], listG iter.Seq[G], listH iter.Seq[H], listI iter.Seq[I]) iter.Seq[lo.Tuple9[A, B, C, D, E, F, G, H, I]] {
return CrossJoinBy9(listA, listB, listC, listD, listE, listF, listG, listH, listI, lo.T9[A, B, C, D, E, F, G, H, I])
}
// CrossJoinBy2 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// Returns an empty list if a list is empty.
func CrossJoinBy2[A, B, Out any](listA iter.Seq[A], listB iter.Seq[B], project func(a A, b B) Out) iter.Seq[Out] {
return func(yield func(Out) bool) {
for a := range listA {
for b := range listB {
if !yield(project(a, b)) {
return
}
}
}
}
}
// CrossJoinBy3 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// Returns an empty list if a list is empty.
func CrossJoinBy3[A, B, C, Out any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], project func(a A, b B, c C) Out) iter.Seq[Out] {
return func(yield func(Out) bool) {
for a := range listA {
for b := range listB {
for c := range listC {
if !yield(project(a, b, c)) {
return
}
}
}
}
}
}
// CrossJoinBy4 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// Returns an empty list if a list is empty.
func CrossJoinBy4[A, B, C, D, Out any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], project func(a A, b B, c C, d D) Out) iter.Seq[Out] {
return func(yield func(Out) bool) {
for a := range listA {
for b := range listB {
for c := range listC {
for d := range listD {
if !yield(project(a, b, c, d)) {
return
}
}
}
}
}
}
}
// CrossJoinBy5 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// Returns an empty list if a list is empty.
func CrossJoinBy5[A, B, C, D, E, Out any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], listE iter.Seq[E], project func(a A, b B, c C, d D, e E) Out) iter.Seq[Out] {
return func(yield func(Out) bool) {
for a := range listA {
for b := range listB {
for c := range listC {
for d := range listD {
for e := range listE {
if !yield(project(a, b, c, d, e)) {
return
}
}
}
}
}
}
}
}
// CrossJoinBy6 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// Returns an empty list if a list is empty.
func CrossJoinBy6[A, B, C, D, E, F, Out any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], listE iter.Seq[E], listF iter.Seq[F], project func(a A, b B, c C, d D, e E, f F) Out) iter.Seq[Out] {
return func(yield func(Out) bool) {
for a := range listA {
for b := range listB {
for c := range listC {
for d := range listD {
for e := range listE {
for f := range listF {
if !yield(project(a, b, c, d, e, f)) {
return
}
}
}
}
}
}
}
}
}
// CrossJoinBy7 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// Returns an empty list if a list is empty.
func CrossJoinBy7[A, B, C, D, E, F, G, Out any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], listE iter.Seq[E], listF iter.Seq[F], listG iter.Seq[G], project func(a A, b B, c C, d D, e E, f F, g G) Out) iter.Seq[Out] {
return func(yield func(Out) bool) {
for a := range listA {
for b := range listB {
for c := range listC {
for d := range listD {
for e := range listE {
for f := range listF {
for g := range listG {
if !yield(project(a, b, c, d, e, f, g)) {
return
}
}
}
}
}
}
}
}
}
}
// CrossJoinBy8 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// Returns an empty list if a list is empty.
func CrossJoinBy8[A, B, C, D, E, F, G, H, Out any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], listE iter.Seq[E], listF iter.Seq[F], listG iter.Seq[G], listH iter.Seq[H], project func(a A, b B, c C, d D, e E, f F, g G, h H) Out) iter.Seq[Out] {
return func(yield func(Out) bool) {
for a := range listA {
for b := range listB {
for c := range listC {
for d := range listD {
for e := range listE {
for f := range listF {
for g := range listG {
for h := range listH {
if !yield(project(a, b, c, d, e, f, g, h)) {
return
}
}
}
}
}
}
}
}
}
}
}
// CrossJoinBy9 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments. The project function
// is used to create the output values.
// Returns an empty list if a list is empty.
func CrossJoinBy9[A, B, C, D, E, F, G, H, I, Out any](listA iter.Seq[A], listB iter.Seq[B], listC iter.Seq[C], listD iter.Seq[D], listE iter.Seq[E], listF iter.Seq[F], listG iter.Seq[G], listH iter.Seq[H], listI iter.Seq[I], project func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) iter.Seq[Out] {
return func(yield func(Out) bool) {
for a := range listA {
for b := range listB {
for c := range listC {
for d := range listD {
for e := range listE {
for f := range listF {
for g := range listG {
for h := range listH {
for i := range listI {
if !yield(project(a, b, c, d, e, f, g, h, i)) {
return
}
}
}
}
}
}
}
}
}
}
}
}
+440
View File
@@ -0,0 +1,440 @@
//go:build go1.23
package it
import (
"fmt"
"slices"
)
func ExampleZip2() {
result := Zip2(values("hello"), values(2))
fmt.Printf("%v", slices.Collect(result))
// Output: [{hello 2}]
}
func ExampleZip3() {
result := Zip3(values("hello"), values(2), values(true))
fmt.Printf("%v", slices.Collect(result))
// Output: [{hello 2 true}]
}
func ExampleZip4() {
result := Zip4(values("hello"), values(2), values(true), values(foo{bar: "bar"}))
fmt.Printf("%v", slices.Collect(result))
// Output: [{hello 2 true {bar}}]
}
func ExampleZip5() {
result := Zip5(values("hello"), values(2), values(true), values(foo{bar: "bar"}), values(4.2))
fmt.Printf("%v", slices.Collect(result))
// Output: [{hello 2 true {bar} 4.2}]
}
func ExampleZip6() {
result := Zip6(values("hello"), values(2), values(true), values(foo{bar: "bar"}), values(4.2), values("plop"))
fmt.Printf("%v", slices.Collect(result))
// Output: [{hello 2 true {bar} 4.2 plop}]
}
func ExampleZip7() {
result := Zip7(values("hello"), values(2), values(true), values(foo{bar: "bar"}), values(4.2), values("plop"), values(false))
fmt.Printf("%v", slices.Collect(result))
// Output: [{hello 2 true {bar} 4.2 plop false}]
}
func ExampleZip8() {
result := Zip8(values("hello"), values(2), values(true), values(foo{bar: "bar"}), values(4.2), values("plop"), values(false), values(42))
fmt.Printf("%v", slices.Collect(result))
// Output: [{hello 2 true {bar} 4.2 plop false 42}]
}
func ExampleZip9() {
result := Zip9(values("hello"), values(2), values(true), values(foo{bar: "bar"}), values(4.2), values("plop"), values(false), values(42), values("hello world"))
fmt.Printf("%v", slices.Collect(result))
// Output: [{hello 2 true {bar} 4.2 plop false 42 hello world}]
}
func ExampleCrossJoin2() {
result := CrossJoin2(values("a", "b"), values(1, 2, 3, 4))
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// {a 1}
// {a 2}
// {a 3}
// {a 4}
// {b 1}
// {b 2}
// {b 3}
// {b 4}
}
func ExampleCrossJoin3() {
result := CrossJoin3(values("a", "b"), values(1, 2, 3, 4), values(true, false))
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// {a 1 true}
// {a 1 false}
// {a 2 true}
// {a 2 false}
// {a 3 true}
// {a 3 false}
// {a 4 true}
// {a 4 false}
// {b 1 true}
// {b 1 false}
// {b 2 true}
// {b 2 false}
// {b 3 true}
// {b 3 false}
// {b 4 true}
// {b 4 false}
}
func ExampleCrossJoin4() {
result := CrossJoin4(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}))
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// {a 1 true {bar}}
// {a 1 false {bar}}
// {a 2 true {bar}}
// {a 2 false {bar}}
// {a 3 true {bar}}
// {a 3 false {bar}}
// {a 4 true {bar}}
// {a 4 false {bar}}
// {b 1 true {bar}}
// {b 1 false {bar}}
// {b 2 true {bar}}
// {b 2 false {bar}}
// {b 3 true {bar}}
// {b 3 false {bar}}
// {b 4 true {bar}}
// {b 4 false {bar}}
}
func ExampleCrossJoin5() {
result := CrossJoin5(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), values(4.2))
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// {a 1 true {bar} 4.2}
// {a 1 false {bar} 4.2}
// {a 2 true {bar} 4.2}
// {a 2 false {bar} 4.2}
// {a 3 true {bar} 4.2}
// {a 3 false {bar} 4.2}
// {a 4 true {bar} 4.2}
// {a 4 false {bar} 4.2}
// {b 1 true {bar} 4.2}
// {b 1 false {bar} 4.2}
// {b 2 true {bar} 4.2}
// {b 2 false {bar} 4.2}
// {b 3 true {bar} 4.2}
// {b 3 false {bar} 4.2}
// {b 4 true {bar} 4.2}
// {b 4 false {bar} 4.2}
}
func ExampleCrossJoin6() {
result := CrossJoin6(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), values(4.2), values("plop"))
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// {a 1 true {bar} 4.2 plop}
// {a 1 false {bar} 4.2 plop}
// {a 2 true {bar} 4.2 plop}
// {a 2 false {bar} 4.2 plop}
// {a 3 true {bar} 4.2 plop}
// {a 3 false {bar} 4.2 plop}
// {a 4 true {bar} 4.2 plop}
// {a 4 false {bar} 4.2 plop}
// {b 1 true {bar} 4.2 plop}
// {b 1 false {bar} 4.2 plop}
// {b 2 true {bar} 4.2 plop}
// {b 2 false {bar} 4.2 plop}
// {b 3 true {bar} 4.2 plop}
// {b 3 false {bar} 4.2 plop}
// {b 4 true {bar} 4.2 plop}
// {b 4 false {bar} 4.2 plop}
}
func ExampleCrossJoin7() {
result := CrossJoin7(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), values(4.2), values("plop"), values(false))
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// {a 1 true {bar} 4.2 plop false}
// {a 1 false {bar} 4.2 plop false}
// {a 2 true {bar} 4.2 plop false}
// {a 2 false {bar} 4.2 plop false}
// {a 3 true {bar} 4.2 plop false}
// {a 3 false {bar} 4.2 plop false}
// {a 4 true {bar} 4.2 plop false}
// {a 4 false {bar} 4.2 plop false}
// {b 1 true {bar} 4.2 plop false}
// {b 1 false {bar} 4.2 plop false}
// {b 2 true {bar} 4.2 plop false}
// {b 2 false {bar} 4.2 plop false}
// {b 3 true {bar} 4.2 plop false}
// {b 3 false {bar} 4.2 plop false}
// {b 4 true {bar} 4.2 plop false}
// {b 4 false {bar} 4.2 plop false}
}
func ExampleCrossJoin8() {
result := CrossJoin8(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), values(4.2), values("plop"), values(false), values(42))
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// {a 1 true {bar} 4.2 plop false 42}
// {a 1 false {bar} 4.2 plop false 42}
// {a 2 true {bar} 4.2 plop false 42}
// {a 2 false {bar} 4.2 plop false 42}
// {a 3 true {bar} 4.2 plop false 42}
// {a 3 false {bar} 4.2 plop false 42}
// {a 4 true {bar} 4.2 plop false 42}
// {a 4 false {bar} 4.2 plop false 42}
// {b 1 true {bar} 4.2 plop false 42}
// {b 1 false {bar} 4.2 plop false 42}
// {b 2 true {bar} 4.2 plop false 42}
// {b 2 false {bar} 4.2 plop false 42}
// {b 3 true {bar} 4.2 plop false 42}
// {b 3 false {bar} 4.2 plop false 42}
// {b 4 true {bar} 4.2 plop false 42}
// {b 4 false {bar} 4.2 plop false 42}
}
func ExampleCrossJoin9() {
result := CrossJoin9(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), values(4.2), values("plop"), values(false), values(42), values("hello world"))
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// {a 1 true {bar} 4.2 plop false 42 hello world}
// {a 1 false {bar} 4.2 plop false 42 hello world}
// {a 2 true {bar} 4.2 plop false 42 hello world}
// {a 2 false {bar} 4.2 plop false 42 hello world}
// {a 3 true {bar} 4.2 plop false 42 hello world}
// {a 3 false {bar} 4.2 plop false 42 hello world}
// {a 4 true {bar} 4.2 plop false 42 hello world}
// {a 4 false {bar} 4.2 plop false 42 hello world}
// {b 1 true {bar} 4.2 plop false 42 hello world}
// {b 1 false {bar} 4.2 plop false 42 hello world}
// {b 2 true {bar} 4.2 plop false 42 hello world}
// {b 2 false {bar} 4.2 plop false 42 hello world}
// {b 3 true {bar} 4.2 plop false 42 hello world}
// {b 3 false {bar} 4.2 plop false 42 hello world}
// {b 4 true {bar} 4.2 plop false 42 hello world}
// {b 4 false {bar} 4.2 plop false 42 hello world}
}
func ExampleCrossJoinBy2() {
result := CrossJoinBy2(values("a", "b"), values(1, 2, 3, 4), func(a string, b int) string {
return fmt.Sprintf("%v-%v", a, b)
})
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// a-1
// a-2
// a-3
// a-4
// b-1
// b-2
// b-3
// b-4
}
func ExampleCrossJoinBy3() {
result := CrossJoinBy3(values("a", "b"), values(1, 2, 3, 4), values(true, false), func(a string, b int, c bool) string {
return fmt.Sprintf("%v-%v-%v", a, b, c)
})
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// a-1-true
// a-1-false
// a-2-true
// a-2-false
// a-3-true
// a-3-false
// a-4-true
// a-4-false
// b-1-true
// b-1-false
// b-2-true
// b-2-false
// b-3-true
// b-3-false
// b-4-true
// b-4-false
}
func ExampleCrossJoinBy4() {
result := CrossJoinBy4(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), func(a string, b int, c bool, d foo) string {
return fmt.Sprintf("%v-%v-%v-%v", a, b, c, d)
})
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// a-1-true-{bar}
// a-1-false-{bar}
// a-2-true-{bar}
// a-2-false-{bar}
// a-3-true-{bar}
// a-3-false-{bar}
// a-4-true-{bar}
// a-4-false-{bar}
// b-1-true-{bar}
// b-1-false-{bar}
// b-2-true-{bar}
// b-2-false-{bar}
// b-3-true-{bar}
// b-3-false-{bar}
// b-4-true-{bar}
// b-4-false-{bar}
}
func ExampleCrossJoinBy5() {
result := CrossJoinBy5(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), values(4.2), func(a string, b int, c bool, d foo, e float64) string {
return fmt.Sprintf("%v-%v-%v-%v-%v", a, b, c, d, e)
})
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// a-1-true-{bar}-4.2
// a-1-false-{bar}-4.2
// a-2-true-{bar}-4.2
// a-2-false-{bar}-4.2
// a-3-true-{bar}-4.2
// a-3-false-{bar}-4.2
// a-4-true-{bar}-4.2
// a-4-false-{bar}-4.2
// b-1-true-{bar}-4.2
// b-1-false-{bar}-4.2
// b-2-true-{bar}-4.2
// b-2-false-{bar}-4.2
// b-3-true-{bar}-4.2
// b-3-false-{bar}-4.2
// b-4-true-{bar}-4.2
// b-4-false-{bar}-4.2
}
func ExampleCrossJoinBy6() {
result := CrossJoinBy6(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), values(4.2), values("plop"), func(a string, b int, c bool, d foo, e float64, f string) string {
return fmt.Sprintf("%v-%v-%v-%v-%v-%v", a, b, c, d, e, f)
})
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// a-1-true-{bar}-4.2-plop
// a-1-false-{bar}-4.2-plop
// a-2-true-{bar}-4.2-plop
// a-2-false-{bar}-4.2-plop
// a-3-true-{bar}-4.2-plop
// a-3-false-{bar}-4.2-plop
// a-4-true-{bar}-4.2-plop
// a-4-false-{bar}-4.2-plop
// b-1-true-{bar}-4.2-plop
// b-1-false-{bar}-4.2-plop
// b-2-true-{bar}-4.2-plop
// b-2-false-{bar}-4.2-plop
// b-3-true-{bar}-4.2-plop
// b-3-false-{bar}-4.2-plop
// b-4-true-{bar}-4.2-plop
// b-4-false-{bar}-4.2-plop
}
func ExampleCrossJoinBy7() {
result := CrossJoinBy7(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), values(4.2), values("plop"), values(false), func(a string, b int, c bool, d foo, e float64, f string, g bool) string {
return fmt.Sprintf("%v-%v-%v-%v-%v-%v-%v", a, b, c, d, e, f, g)
})
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// a-1-true-{bar}-4.2-plop-false
// a-1-false-{bar}-4.2-plop-false
// a-2-true-{bar}-4.2-plop-false
// a-2-false-{bar}-4.2-plop-false
// a-3-true-{bar}-4.2-plop-false
// a-3-false-{bar}-4.2-plop-false
// a-4-true-{bar}-4.2-plop-false
// a-4-false-{bar}-4.2-plop-false
// b-1-true-{bar}-4.2-plop-false
// b-1-false-{bar}-4.2-plop-false
// b-2-true-{bar}-4.2-plop-false
// b-2-false-{bar}-4.2-plop-false
// b-3-true-{bar}-4.2-plop-false
// b-3-false-{bar}-4.2-plop-false
// b-4-true-{bar}-4.2-plop-false
// b-4-false-{bar}-4.2-plop-false
}
func ExampleCrossJoinBy8() {
result := CrossJoinBy8(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), values(4.2), values("plop"), values(false), values(42), func(a string, b int, c bool, d foo, e float64, f string, g bool, h int) string {
return fmt.Sprintf("%v-%v-%v-%v-%v-%v-%v-%v", a, b, c, d, e, f, g, h)
})
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// a-1-true-{bar}-4.2-plop-false-42
// a-1-false-{bar}-4.2-plop-false-42
// a-2-true-{bar}-4.2-plop-false-42
// a-2-false-{bar}-4.2-plop-false-42
// a-3-true-{bar}-4.2-plop-false-42
// a-3-false-{bar}-4.2-plop-false-42
// a-4-true-{bar}-4.2-plop-false-42
// a-4-false-{bar}-4.2-plop-false-42
// b-1-true-{bar}-4.2-plop-false-42
// b-1-false-{bar}-4.2-plop-false-42
// b-2-true-{bar}-4.2-plop-false-42
// b-2-false-{bar}-4.2-plop-false-42
// b-3-true-{bar}-4.2-plop-false-42
// b-3-false-{bar}-4.2-plop-false-42
// b-4-true-{bar}-4.2-plop-false-42
// b-4-false-{bar}-4.2-plop-false-42
}
func ExampleCrossJoinBy9() {
result := CrossJoinBy9(values("a", "b"), values(1, 2, 3, 4), values(true, false), values(foo{bar: "bar"}), values(4.2), values("plop"), values(false), values(42), values("hello world"), func(a string, b int, c bool, d foo, e float64, f string, g bool, h int, i string) string {
return fmt.Sprintf("%v-%v-%v-%v-%v-%v-%v-%v-%v", a, b, c, d, e, f, g, h, i)
})
for r := range result {
fmt.Printf("%v\n", r)
}
// Output:
// a-1-true-{bar}-4.2-plop-false-42-hello world
// a-1-false-{bar}-4.2-plop-false-42-hello world
// a-2-true-{bar}-4.2-plop-false-42-hello world
// a-2-false-{bar}-4.2-plop-false-42-hello world
// a-3-true-{bar}-4.2-plop-false-42-hello world
// a-3-false-{bar}-4.2-plop-false-42-hello world
// a-4-true-{bar}-4.2-plop-false-42-hello world
// a-4-false-{bar}-4.2-plop-false-42-hello world
// b-1-true-{bar}-4.2-plop-false-42-hello world
// b-1-false-{bar}-4.2-plop-false-42-hello world
// b-2-true-{bar}-4.2-plop-false-42-hello world
// b-2-false-{bar}-4.2-plop-false-42-hello world
// b-3-true-{bar}-4.2-plop-false-42-hello world
// b-3-false-{bar}-4.2-plop-false-42-hello world
// b-4-true-{bar}-4.2-plop-false-42-hello world
// b-4-false-{bar}-4.2-plop-false-42-hello world
}
+363
View File
@@ -0,0 +1,363 @@
//go:build go1.23
package it
import (
"slices"
"testing"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
)
func TestZip(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := Zip2(
values("a", "b"),
values(1, 2),
)
r2 := Zip3(
values("a", "b", "c"),
values(1, 2, 3),
values(4, 5, 6),
)
r3 := Zip4(
values("a", "b", "c", "d"),
values(1, 2, 3, 4),
values(5, 6, 7, 8),
values(true, true, true, true),
)
r4 := Zip5(
values("a", "b", "c", "d", "e"),
values(1, 2, 3, 4, 5),
values(6, 7, 8, 9, 10),
values(true, true, true, true, true),
values[float32](0.1, 0.2, 0.3, 0.4, 0.5),
)
r5 := Zip6(
values("a", "b", "c", "d", "e", "f"),
values(1, 2, 3, 4, 5, 6),
values(7, 8, 9, 10, 11, 12),
values(true, true, true, true, true, true),
values[float32](0.1, 0.2, 0.3, 0.4, 0.5, 0.6),
values(0.01, 0.02, 0.03, 0.04, 0.05, 0.06),
)
r6 := Zip7(
values("a", "b", "c", "d", "e", "f", "g"),
values(1, 2, 3, 4, 5, 6, 7),
values(8, 9, 10, 11, 12, 13, 14),
values(true, true, true, true, true, true, true),
values[float32](0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7),
values(0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07),
values[int8](1, 2, 3, 4, 5, 6, 7),
)
r7 := Zip8(
values("a", "b", "c", "d", "e", "f", "g", "h"),
values(1, 2, 3, 4, 5, 6, 7, 8),
values(9, 10, 11, 12, 13, 14, 15, 16),
values(true, true, true, true, true, true, true, true),
values[float32](0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8),
values(0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08),
values[int8](1, 2, 3, 4, 5, 6, 7, 8),
values[int16](1, 2, 3, 4, 5, 6, 7, 8),
)
r8 := Zip9(
values("a", "b", "c", "d", "e", "f", "g", "h", "i"),
values(1, 2, 3, 4, 5, 6, 7, 8, 9),
values(10, 11, 12, 13, 14, 15, 16, 17, 18),
values(true, true, true, true, true, true, true, true, true),
values[float32](0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9),
values(0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09),
values[int8](1, 2, 3, 4, 5, 6, 7, 8, 9),
values[int16](1, 2, 3, 4, 5, 6, 7, 8, 9),
values[int32](1, 2, 3, 4, 5, 6, 7, 8, 9),
)
is.Equal([]lo.Tuple2[string, int]{
{A: "a", B: 1},
{A: "b", B: 2},
}, slices.Collect(r1))
is.Equal([]lo.Tuple3[string, int, int]{
{A: "a", B: 1, C: 4},
{A: "b", B: 2, C: 5},
{A: "c", B: 3, C: 6},
}, slices.Collect(r2))
is.Equal([]lo.Tuple4[string, int, int, bool]{
{A: "a", B: 1, C: 5, D: true},
{A: "b", B: 2, C: 6, D: true},
{A: "c", B: 3, C: 7, D: true},
{A: "d", B: 4, C: 8, D: true},
}, slices.Collect(r3))
is.Equal([]lo.Tuple5[string, int, int, bool, float32]{
{A: "a", B: 1, C: 6, D: true, E: 0.1},
{A: "b", B: 2, C: 7, D: true, E: 0.2},
{A: "c", B: 3, C: 8, D: true, E: 0.3},
{A: "d", B: 4, C: 9, D: true, E: 0.4},
{A: "e", B: 5, C: 10, D: true, E: 0.5},
}, slices.Collect(r4))
is.Equal([]lo.Tuple6[string, int, int, bool, float32, float64]{
{A: "a", B: 1, C: 7, D: true, E: 0.1, F: 0.01},
{A: "b", B: 2, C: 8, D: true, E: 0.2, F: 0.02},
{A: "c", B: 3, C: 9, D: true, E: 0.3, F: 0.03},
{A: "d", B: 4, C: 10, D: true, E: 0.4, F: 0.04},
{A: "e", B: 5, C: 11, D: true, E: 0.5, F: 0.05},
{A: "f", B: 6, C: 12, D: true, E: 0.6, F: 0.06},
}, slices.Collect(r5))
is.Equal([]lo.Tuple7[string, int, int, bool, float32, float64, int8]{
{A: "a", B: 1, C: 8, D: true, E: 0.1, F: 0.01, G: 1},
{A: "b", B: 2, C: 9, D: true, E: 0.2, F: 0.02, G: 2},
{A: "c", B: 3, C: 10, D: true, E: 0.3, F: 0.03, G: 3},
{A: "d", B: 4, C: 11, D: true, E: 0.4, F: 0.04, G: 4},
{A: "e", B: 5, C: 12, D: true, E: 0.5, F: 0.05, G: 5},
{A: "f", B: 6, C: 13, D: true, E: 0.6, F: 0.06, G: 6},
{A: "g", B: 7, C: 14, D: true, E: 0.7, F: 0.07, G: 7},
}, slices.Collect(r6))
is.Equal([]lo.Tuple8[string, int, int, bool, float32, float64, int8, int16]{
{A: "a", B: 1, C: 9, D: true, E: 0.1, F: 0.01, G: 1, H: 1},
{A: "b", B: 2, C: 10, D: true, E: 0.2, F: 0.02, G: 2, H: 2},
{A: "c", B: 3, C: 11, D: true, E: 0.3, F: 0.03, G: 3, H: 3},
{A: "d", B: 4, C: 12, D: true, E: 0.4, F: 0.04, G: 4, H: 4},
{A: "e", B: 5, C: 13, D: true, E: 0.5, F: 0.05, G: 5, H: 5},
{A: "f", B: 6, C: 14, D: true, E: 0.6, F: 0.06, G: 6, H: 6},
{A: "g", B: 7, C: 15, D: true, E: 0.7, F: 0.07, G: 7, H: 7},
{A: "h", B: 8, C: 16, D: true, E: 0.8, F: 0.08, G: 8, H: 8},
}, slices.Collect(r7))
is.Equal([]lo.Tuple9[string, int, int, bool, float32, float64, int8, int16, int32]{
{A: "a", B: 1, C: 10, D: true, E: 0.1, F: 0.01, G: 1, H: 1, I: 1},
{A: "b", B: 2, C: 11, D: true, E: 0.2, F: 0.02, G: 2, H: 2, I: 2},
{A: "c", B: 3, C: 12, D: true, E: 0.3, F: 0.03, G: 3, H: 3, I: 3},
{A: "d", B: 4, C: 13, D: true, E: 0.4, F: 0.04, G: 4, H: 4, I: 4},
{A: "e", B: 5, C: 14, D: true, E: 0.5, F: 0.05, G: 5, H: 5, I: 5},
{A: "f", B: 6, C: 15, D: true, E: 0.6, F: 0.06, G: 6, H: 6, I: 6},
{A: "g", B: 7, C: 16, D: true, E: 0.7, F: 0.07, G: 7, H: 7, I: 7},
{A: "h", B: 8, C: 17, D: true, E: 0.8, F: 0.08, G: 8, H: 8, I: 8},
{A: "i", B: 9, C: 18, D: true, E: 0.9, F: 0.09, G: 9, H: 9, I: 9},
}, slices.Collect(r8))
}
func TestZipBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := ZipBy2(
values("a", "b"),
values(1, 2),
lo.T2[string, int],
)
r2 := ZipBy3(
values("a", "b", "c"),
values(1, 2, 3),
values(4, 5, 6),
lo.T3[string, int, int],
)
r3 := ZipBy4(
values("a", "b", "c", "d"),
values(1, 2, 3, 4),
values(5, 6, 7, 8),
values(true, true, true, true),
lo.T4[string, int, int, bool],
)
r4 := ZipBy5(
values("a", "b", "c", "d", "e"),
values(1, 2, 3, 4, 5),
values(6, 7, 8, 9, 10),
values(true, true, true, true, true),
values[float32](0.1, 0.2, 0.3, 0.4, 0.5),
lo.T5[string, int, int, bool, float32],
)
r5 := ZipBy6(
values("a", "b", "c", "d", "e", "f"),
values(1, 2, 3, 4, 5, 6),
values(7, 8, 9, 10, 11, 12),
values(true, true, true, true, true, true),
values[float32](0.1, 0.2, 0.3, 0.4, 0.5, 0.6),
values(0.01, 0.02, 0.03, 0.04, 0.05, 0.06),
lo.T6[string, int, int, bool, float32, float64],
)
r6 := ZipBy7(
values("a", "b", "c", "d", "e", "f", "g"),
values(1, 2, 3, 4, 5, 6, 7),
values(8, 9, 10, 11, 12, 13, 14),
values(true, true, true, true, true, true, true),
values[float32](0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7),
values(0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07),
values[int8](1, 2, 3, 4, 5, 6, 7),
lo.T7[string, int, int, bool, float32, float64, int8],
)
r7 := ZipBy8(
values("a", "b", "c", "d", "e", "f", "g", "h"),
values(1, 2, 3, 4, 5, 6, 7, 8),
values(9, 10, 11, 12, 13, 14, 15, 16),
values(true, true, true, true, true, true, true, true),
values[float32](0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8),
values(0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08),
values[int8](1, 2, 3, 4, 5, 6, 7, 8),
values[int16](1, 2, 3, 4, 5, 6, 7, 8),
lo.T8[string, int, int, bool, float32, float64, int8, int16],
)
r8 := ZipBy9(
values("a", "b", "c", "d", "e", "f", "g", "h", "i"),
values(1, 2, 3, 4, 5, 6, 7, 8, 9),
values(10, 11, 12, 13, 14, 15, 16, 17, 18),
values(true, true, true, true, true, true, true, true, true),
values[float32](0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9),
values(0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09),
values[int8](1, 2, 3, 4, 5, 6, 7, 8, 9),
values[int16](1, 2, 3, 4, 5, 6, 7, 8, 9),
values[int32](1, 2, 3, 4, 5, 6, 7, 8, 9),
lo.T9[string, int, int, bool, float32, float64, int8, int16, int32],
)
is.Equal([]lo.Tuple2[string, int]{
{A: "a", B: 1},
{A: "b", B: 2},
}, slices.Collect(r1))
is.Equal([]lo.Tuple3[string, int, int]{
{A: "a", B: 1, C: 4},
{A: "b", B: 2, C: 5},
{A: "c", B: 3, C: 6},
}, slices.Collect(r2))
is.Equal([]lo.Tuple4[string, int, int, bool]{
{A: "a", B: 1, C: 5, D: true},
{A: "b", B: 2, C: 6, D: true},
{A: "c", B: 3, C: 7, D: true},
{A: "d", B: 4, C: 8, D: true},
}, slices.Collect(r3))
is.Equal([]lo.Tuple5[string, int, int, bool, float32]{
{A: "a", B: 1, C: 6, D: true, E: 0.1},
{A: "b", B: 2, C: 7, D: true, E: 0.2},
{A: "c", B: 3, C: 8, D: true, E: 0.3},
{A: "d", B: 4, C: 9, D: true, E: 0.4},
{A: "e", B: 5, C: 10, D: true, E: 0.5},
}, slices.Collect(r4))
is.Equal([]lo.Tuple6[string, int, int, bool, float32, float64]{
{A: "a", B: 1, C: 7, D: true, E: 0.1, F: 0.01},
{A: "b", B: 2, C: 8, D: true, E: 0.2, F: 0.02},
{A: "c", B: 3, C: 9, D: true, E: 0.3, F: 0.03},
{A: "d", B: 4, C: 10, D: true, E: 0.4, F: 0.04},
{A: "e", B: 5, C: 11, D: true, E: 0.5, F: 0.05},
{A: "f", B: 6, C: 12, D: true, E: 0.6, F: 0.06},
}, slices.Collect(r5))
is.Equal([]lo.Tuple7[string, int, int, bool, float32, float64, int8]{
{A: "a", B: 1, C: 8, D: true, E: 0.1, F: 0.01, G: 1},
{A: "b", B: 2, C: 9, D: true, E: 0.2, F: 0.02, G: 2},
{A: "c", B: 3, C: 10, D: true, E: 0.3, F: 0.03, G: 3},
{A: "d", B: 4, C: 11, D: true, E: 0.4, F: 0.04, G: 4},
{A: "e", B: 5, C: 12, D: true, E: 0.5, F: 0.05, G: 5},
{A: "f", B: 6, C: 13, D: true, E: 0.6, F: 0.06, G: 6},
{A: "g", B: 7, C: 14, D: true, E: 0.7, F: 0.07, G: 7},
}, slices.Collect(r6))
is.Equal([]lo.Tuple8[string, int, int, bool, float32, float64, int8, int16]{
{A: "a", B: 1, C: 9, D: true, E: 0.1, F: 0.01, G: 1, H: 1},
{A: "b", B: 2, C: 10, D: true, E: 0.2, F: 0.02, G: 2, H: 2},
{A: "c", B: 3, C: 11, D: true, E: 0.3, F: 0.03, G: 3, H: 3},
{A: "d", B: 4, C: 12, D: true, E: 0.4, F: 0.04, G: 4, H: 4},
{A: "e", B: 5, C: 13, D: true, E: 0.5, F: 0.05, G: 5, H: 5},
{A: "f", B: 6, C: 14, D: true, E: 0.6, F: 0.06, G: 6, H: 6},
{A: "g", B: 7, C: 15, D: true, E: 0.7, F: 0.07, G: 7, H: 7},
{A: "h", B: 8, C: 16, D: true, E: 0.8, F: 0.08, G: 8, H: 8},
}, slices.Collect(r7))
is.Equal([]lo.Tuple9[string, int, int, bool, float32, float64, int8, int16, int32]{
{A: "a", B: 1, C: 10, D: true, E: 0.1, F: 0.01, G: 1, H: 1, I: 1},
{A: "b", B: 2, C: 11, D: true, E: 0.2, F: 0.02, G: 2, H: 2, I: 2},
{A: "c", B: 3, C: 12, D: true, E: 0.3, F: 0.03, G: 3, H: 3, I: 3},
{A: "d", B: 4, C: 13, D: true, E: 0.4, F: 0.04, G: 4, H: 4, I: 4},
{A: "e", B: 5, C: 14, D: true, E: 0.5, F: 0.05, G: 5, H: 5, I: 5},
{A: "f", B: 6, C: 15, D: true, E: 0.6, F: 0.06, G: 6, H: 6, I: 6},
{A: "g", B: 7, C: 16, D: true, E: 0.7, F: 0.07, G: 7, H: 7, I: 7},
{A: "h", B: 8, C: 17, D: true, E: 0.8, F: 0.08, G: 8, H: 8, I: 8},
{A: "i", B: 9, C: 18, D: true, E: 0.9, F: 0.09, G: 9, H: 9, I: 9},
}, slices.Collect(r8))
}
func TestCrossJoin(t *testing.T) {
t.Parallel()
is := assert.New(t)
listOne := values("a", "b", "c")
listTwo := values(1, 2, 3)
emptyList := values[any]()
mixedList := values[any](9.6, 4, "foobar")
results1 := CrossJoin2(emptyList, listTwo)
is.Empty(slices.Collect(results1))
results2 := CrossJoin2(listOne, emptyList)
is.Empty(slices.Collect(results2))
results3 := CrossJoin2(emptyList, emptyList)
is.Empty(slices.Collect(results3))
results4 := CrossJoin2(values("a"), listTwo)
is.Equal([]lo.Tuple2[string, int]{lo.T2("a", 1), lo.T2("a", 2), lo.T2("a", 3)}, slices.Collect(results4))
results5 := CrossJoin2(listOne, values(1))
is.Equal([]lo.Tuple2[string, int]{lo.T2("a", 1), lo.T2("b", 1), lo.T2("c", 1)}, slices.Collect(results5))
results6 := CrossJoin2(listOne, listTwo)
is.Equal([]lo.Tuple2[string, int]{lo.T2("a", 1), lo.T2("a", 2), lo.T2("a", 3), lo.T2("b", 1), lo.T2("b", 2), lo.T2("b", 3), lo.T2("c", 1), lo.T2("c", 2), lo.T2("c", 3)}, slices.Collect(results6))
results7 := CrossJoin2(listOne, mixedList)
is.Equal([]lo.Tuple2[string, any]{lo.T2[string, any]("a", 9.6), lo.T2[string, any]("a", 4), lo.T2[string, any]("a", "foobar"), lo.T2[string, any]("b", 9.6), lo.T2[string, any]("b", 4), lo.T2[string, any]("b", "foobar"), lo.T2[string, any]("c", 9.6), lo.T2[string, any]("c", 4), lo.T2[string, any]("c", "foobar")}, slices.Collect(results7))
}
func TestCrossJoinBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
listOne := values("a", "b", "c")
listTwo := values(1, 2, 3)
emptyList := values[any]()
mixedList := values[any](9.6, 4, "foobar")
results1 := CrossJoinBy2(emptyList, listTwo, lo.T2[any, int])
is.Empty(slices.Collect(results1))
results2 := CrossJoinBy2(listOne, emptyList, lo.T2[string, any])
is.Empty(slices.Collect(results2))
results3 := CrossJoinBy2(emptyList, emptyList, lo.T2[any, any])
is.Empty(slices.Collect(results3))
results4 := CrossJoinBy2(values("a"), listTwo, lo.T2[string, int])
is.Equal([]lo.Tuple2[string, int]{lo.T2("a", 1), lo.T2("a", 2), lo.T2("a", 3)}, slices.Collect(results4))
results5 := CrossJoinBy2(listOne, values(1), lo.T2[string, int])
is.Equal([]lo.Tuple2[string, int]{lo.T2("a", 1), lo.T2("b", 1), lo.T2("c", 1)}, slices.Collect(results5))
results6 := CrossJoinBy2(listOne, listTwo, lo.T2[string, int])
is.Equal([]lo.Tuple2[string, int]{lo.T2("a", 1), lo.T2("a", 2), lo.T2("a", 3), lo.T2("b", 1), lo.T2("b", 2), lo.T2("b", 3), lo.T2("c", 1), lo.T2("c", 2), lo.T2("c", 3)}, slices.Collect(results6))
results7 := CrossJoinBy2(listOne, mixedList, lo.T2[string, any])
is.Equal([]lo.Tuple2[string, any]{lo.T2[string, any]("a", 9.6), lo.T2[string, any]("a", 4), lo.T2[string, any]("a", "foobar"), lo.T2[string, any]("b", 9.6), lo.T2[string, any]("b", 4), lo.T2[string, any]("b", "foobar"), lo.T2[string, any]("c", 9.6), lo.T2[string, any]("c", 4), lo.T2[string, any]("c", "foobar")}, slices.Collect(results7))
}
+79
View File
@@ -0,0 +1,79 @@
//go:build go1.23
package it
import (
"iter"
"github.com/samber/lo"
)
// ToSeqPtr returns a sequence of pointers to each value.
func ToSeqPtr[T any](collection iter.Seq[T]) iter.Seq[*T] {
return Map(collection, lo.ToPtr)
}
// FromSeqPtr returns a sequence with the pointer values.
// Returns a zero value in case of a nil pointer element.
func FromSeqPtr[T any](collection iter.Seq[*T]) iter.Seq[T] {
return Map(collection, lo.FromPtr)
}
// FromSeqPtrOr returns a sequence with the pointer values or the fallback value.
func FromSeqPtrOr[T any](collection iter.Seq[*T], fallback T) iter.Seq[T] {
return Map(collection, func(x *T) T { return lo.FromPtrOr(x, fallback) })
}
// ToAnySeq returns a sequence with all elements mapped to `any` type.
func ToAnySeq[T any](collection iter.Seq[T]) iter.Seq[any] {
return Map(collection, func(x T) any { return x })
}
// FromAnySeq returns a sequence with all elements mapped to a type.
// Panics on type conversion failure.
func FromAnySeq[T any](collection iter.Seq[any]) iter.Seq[T] {
return Map(collection, func(item any) T {
if t, ok := item.(T); ok {
return t
}
panic("it.FromAnySeq: type conversion failed")
})
}
// Empty returns an empty sequence.
func Empty[T any]() iter.Seq[T] {
return func(yield func(T) bool) {}
}
// IsEmpty returns true if the sequence is empty.
// Will iterate at most once.
func IsEmpty[T any](collection iter.Seq[T]) bool {
for range collection {
return false
}
return true
}
// IsNotEmpty returns true if the sequence is not empty.
// Will iterate at most once.
func IsNotEmpty[T any](collection iter.Seq[T]) bool {
return !IsEmpty(collection)
}
// CoalesceSeq returns the first non-empty sequence.
// Will iterate through each sub-sequence at most once.
func CoalesceSeq[T any](v ...iter.Seq[T]) (iter.Seq[T], bool) {
for i := range v {
for range v[i] {
return v[i], true
}
}
return Empty[T](), false
}
// CoalesceSeqOrEmpty returns the first non-empty sequence.
// Will iterate through each sub-sequence at most once.
func CoalesceSeqOrEmpty[T any](v ...iter.Seq[T]) iter.Seq[T] {
result, _ := CoalesceSeq(v...)
return result
}
+167
View File
@@ -0,0 +1,167 @@
//go:build go1.23
package it
import (
"slices"
"testing"
"github.com/stretchr/testify/assert"
)
func TestToSeqPtr(t *testing.T) {
t.Parallel()
is := assert.New(t)
str1 := "foo"
str2 := "bar"
result1 := ToSeqPtr(values(str1, str2))
is.Equal([]*string{&str1, &str2}, slices.Collect(result1))
}
func TestFromSeqPtr(t *testing.T) {
t.Parallel()
is := assert.New(t)
str1 := "foo"
str2 := "bar"
result1 := FromSeqPtr(values(&str1, &str2, nil))
is.Equal([]string{str1, str2, ""}, slices.Collect(result1))
}
func TestFromSeqPtrOr(t *testing.T) {
t.Parallel()
is := assert.New(t)
str1 := "foo"
str2 := "bar"
result1 := FromSeqPtrOr(values(&str1, &str2, nil), "fallback")
is.Equal([]string{str1, str2, "fallback"}, slices.Collect(result1))
}
func TestToAnySeq(t *testing.T) {
t.Parallel()
is := assert.New(t)
in1 := values(0, 1, 2, 3)
in2 := values[int]()
out1 := ToAnySeq(in1)
out2 := ToAnySeq(in2)
is.Equal([]any{0, 1, 2, 3}, slices.Collect(out1))
is.Empty(slices.Collect(out2))
}
func TestFromAnySeq(t *testing.T) {
t.Parallel()
is := assert.New(t)
out1 := FromAnySeq[string](values[any]("foobar", 42))
out2 := FromAnySeq[string](values[any]("foobar", "42"))
is.PanicsWithValue("it.FromAnySeq: type conversion failed", func() { _ = slices.Collect(out1) })
is.Equal([]string{"foobar", "42"}, slices.Collect(out2))
}
func TestEmpty(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.Empty(slices.Collect(Empty[string]()))
}
func TestIsEmpty(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.True(IsEmpty(values[string]()))
is.False(IsEmpty(values("foo")))
}
func TestIsNotEmpty(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.False(IsNotEmpty(values[string]()))
is.True(IsNotEmpty(values("foo")))
}
func TestCoalesceSeq(t *testing.T) {
t.Parallel()
is := assert.New(t)
seq0 := values[int]()
seq1 := values(1)
seq2 := values(1, 2)
result1, ok1 := CoalesceSeq[int]()
result4, ok4 := CoalesceSeq(seq0)
result6, ok6 := CoalesceSeq(seq2)
result7, ok7 := CoalesceSeq(seq1)
result8, ok8 := CoalesceSeq(seq1, seq2)
result9, ok9 := CoalesceSeq(seq2, seq1)
result10, ok10 := CoalesceSeq(seq0, seq1, seq2)
is.NotNil(result1)
is.Empty(slices.Collect(result1))
is.False(ok1)
is.NotNil(result4)
is.Empty(slices.Collect(result4))
is.False(ok4)
is.NotNil(result6)
is.Equal(slices.Collect(seq2), slices.Collect(result6))
is.True(ok6)
is.NotNil(result7)
is.Equal(slices.Collect(seq1), slices.Collect(result7))
is.True(ok7)
is.NotNil(result8)
is.Equal(slices.Collect(seq1), slices.Collect(result8))
is.True(ok8)
is.NotNil(result9)
is.Equal(slices.Collect(seq2), slices.Collect(result9))
is.True(ok9)
is.NotNil(result10)
is.Equal(slices.Collect(seq1), slices.Collect(result10))
is.True(ok10)
}
func TestCoalesceSeqOrEmpty(t *testing.T) {
t.Parallel()
is := assert.New(t)
seq0 := values[int]()
seq1 := values(1)
seq2 := values(1, 2)
result1 := CoalesceSeqOrEmpty[int]()
result4 := CoalesceSeqOrEmpty(seq0)
result6 := CoalesceSeqOrEmpty(seq2)
result7 := CoalesceSeqOrEmpty(seq1)
result8 := CoalesceSeqOrEmpty(seq1, seq2)
result9 := CoalesceSeqOrEmpty(seq2, seq1)
result10 := CoalesceSeqOrEmpty(seq0, seq1, seq2)
is.NotNil(result1)
is.Empty(slices.Collect(result1))
is.NotNil(result4)
is.Empty(slices.Collect(result4))
is.NotNil(result6)
is.Equal(slices.Collect(seq2), slices.Collect(result6))
is.NotNil(result7)
is.Equal(slices.Collect(seq1), slices.Collect(result7))
is.NotNil(result8)
is.Equal(slices.Collect(seq1), slices.Collect(result8))
is.NotNil(result9)
is.Equal(slices.Collect(seq2), slices.Collect(result9))
is.NotNil(result10)
is.Equal(slices.Collect(seq1), slices.Collect(result10))
}