mirror of
https://github.com/samber/lo.git
synced 2026-04-22 23:47:11 +08:00
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:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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))
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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}]
|
||||
}
|
||||
@@ -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 },
|
||||
))
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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:])
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
Reference in New Issue
Block a user