From 06a2ea85fff71db3ca4d8791183ccacf457793b6 Mon Sep 17 00:00:00 2001 From: dashjay Date: Thu, 24 Jul 2025 01:22:40 +0800 Subject: [PATCH 1/8] xslice --- pkg/xslice/README.md | 46 ++++++++++++++--------------- pkg/xslice/xslice.go | 6 +--- pkg/xslice/xslice_benchmark_test.go | 16 ++++++++++ 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/pkg/xslice/README.md b/pkg/xslice/README.md index 6d8b8ed..bc866e2 100644 --- a/pkg/xslice/README.md +++ b/pkg/xslice/README.md @@ -140,7 +140,7 @@ xslice.AvgN() 👉 float(0) ``` -## func [Chunk]() +## func [Chunk]() ```go func Chunk[T any, Slice ~[]T](in Slice, chunkSize int) []Slice @@ -157,7 +157,7 @@ xslice.Chunk([]int{1, 2, 3, 4, 5}, 0) 👉 []int{} ``` -## func [ChunkInPlace]() +## func [ChunkInPlace]() ```go func ChunkInPlace[T any, Slice ~[]T](in Slice, chunkSize int) []Slice @@ -172,7 +172,7 @@ xslice.Chunk([]int{1, 2, 3, 4, 5}, 0) 👉 []int{} ``` -## func [Clone]() +## func [Clone]() ```go func Clone[T any](in []T) []T @@ -187,7 +187,7 @@ xslice.Clone([]int{1, 2, 3}) 👉 [1, 2, 3] ``` -## func [CloneBy]() +## func [CloneBy]() ```go func CloneBy[T any, U any](in []T, f func(T) U) []U @@ -203,7 +203,7 @@ xslice.CloneBy([]int{1, 2, 3}, strconv.Itoa) 👉 ["1", "2", "3"] ``` -## func [Concat]() +## func [Concat]() ```go func Concat[T any](vs ...[]T) []T @@ -308,7 +308,7 @@ xslice.Count([]int{}) 👉 0 ``` -## func [Filter]() +## func [Filter]() ```go func Filter[T any, Slice ~[]T](in Slice, f func(T) bool) Slice @@ -399,7 +399,7 @@ Output: ``` -## func [GroupBy]() +## func [GroupBy]() ```go func GroupBy[T any, K comparable, Slice ~[]T](in Slice, f func(T) K) map[K]Slice @@ -414,7 +414,7 @@ xslice.GroupBy([]int{1, 2, 3, 2, 4}, func(x int) int { return x % 2 }) 👉 map[ ``` -## func [GroupByMap]() +## func [GroupByMap]() ```go func GroupByMap[T any, Slice ~[]T, K comparable, V any](in Slice, f func(T) (K, V)) map[K][]V @@ -461,7 +461,7 @@ xslice.HeadO(_range(0, 0)).Ok() 👉 false ``` -## func [Index]() +## func [Index]() ```go func Index[T comparable, Slice ~[]T](in Slice, v T) int @@ -600,7 +600,7 @@ xslice.MinN(1, 2, 3) 👉 1 ``` -## func [Repeat]() +## func [Repeat]() ```go func Repeat[T any, Slice ~[]T](in Slice, count int) Slice @@ -616,7 +616,7 @@ xslice.Repeat([]int{1, 2, 3}, 0) 👉 []int{} ``` -## func [RepeatBy]() +## func [RepeatBy]() ```go func RepeatBy[T any](n int, f func(i int) T) []T @@ -632,7 +632,7 @@ xslice.RepeatBy(3, func(i int) string { return strconv.Itoa(i) }) 👉 []string{ ``` -## func [Replace]() +## func [Replace]() ```go func Replace[T comparable, Slice ~[]T](in Slice, from, to T, count int) []T @@ -648,7 +648,7 @@ xslice.Replace([]int{1, 2, 2}, 2, 4, -1) 👉 [1, 4, 4] ``` -## func [ReplaceAll]() +## func [ReplaceAll]() ```go func ReplaceAll[T comparable, Slice ~[]T](in Slice, from, to T) []T @@ -664,7 +664,7 @@ xslice.ReplaceAll([]int{1, 2, 2}, 2, 4) 👉 [1, 4, 4] ``` -## func [Reverse]() +## func [Reverse]() ```go func Reverse[T any, Slice ~[]T](in Slice) @@ -680,7 +680,7 @@ xslice.Reverse([]int{}) 👉 []int{} ``` -## func [ReverseClone]() +## func [ReverseClone]() ```go func ReverseClone[T any, Slice ~[]T](in Slice) Slice @@ -697,7 +697,7 @@ xslice.ReverseClone([]int{3, 2, 1}) 👉 [1, 2, 3] ``` -## func [Shuffle]() +## func [Shuffle]() ```go func Shuffle[T any, Slice ~[]T](in Slice) Slice @@ -713,7 +713,7 @@ xslice.Shuffle([]int{}) 👉 []int{} ``` -## func [ShuffleInPlace]() +## func [ShuffleInPlace]() ```go func ShuffleInPlace[T any, Slice ~[]T](in Slice) @@ -729,7 +729,7 @@ xslice.ShuffleInPlace(array) 👉 [2, 1, 3] (random) ``` -## func [Subset]() +## func [Subset]() ```go func Subset[T any, Slice ~[]T](in Slice, start, count int) Slice @@ -745,7 +745,7 @@ xslice.Subset([]int{1, 2, 3}, -1, 2) 👉 [2, 3] ``` -## func [SubsetInPlace]() +## func [SubsetInPlace]() ```go func SubsetInPlace[T any, Slice ~[]T](in Slice, start int, count int) Slice @@ -759,7 +759,7 @@ xslice.SubsetInPlace([]int{1, 2, 3}, -1, 2) 👉 [2, 3] ``` -## func [Sum]() +## func [Sum]() ```go func Sum[T constraints.Number, Slice ~[]T](in Slice) T @@ -775,7 +775,7 @@ xslice.Sum([]int{}) 👉 0 ``` -## func [SumBy]() +## func [SumBy]() ```go func SumBy[T any, R constraints.Number, Slice ~[]T](in Slice, f func(T) R) R @@ -794,7 +794,7 @@ xslice.SumBy([]string{}, func(x string) int { return 0 }) 👉 0 ``` -## func [SumN]() +## func [SumN]() ```go func SumN[T constraints.Number](in ...T) T @@ -810,7 +810,7 @@ xslice.SumN() 👉 0 ``` -## func [Uniq]() +## func [Uniq]() ```go func Uniq[T comparable, Slice ~[]T](in Slice) Slice diff --git a/pkg/xslice/xslice.go b/pkg/xslice/xslice.go index 5a321c4..f1d0acf 100644 --- a/pkg/xslice/xslice.go +++ b/pkg/xslice/xslice.go @@ -267,11 +267,7 @@ func MaxBy[T constraints.Ordered](in []T, f func(T, T) bool) optional.O[T] { // xslice.Map([]int{1, 2, 3}, func(x int) int { return x * 2 }) 👉 [2, 4, 6] // xslice.Map([]int{1, 2, 3}, strconv.Itoa) 👉 ["1", "2", "3"] func Map[T any, U any](in []T, f func(T) U) []U { - out := make([]U, len(in)) - for i := range in { - out[i] = f(in[i]) - } - return out + return xiter.ToSlice(xiter.Map(f, xiter.FromSlice(in))) } // Clone returns a copy of the slice. diff --git a/pkg/xslice/xslice_benchmark_test.go b/pkg/xslice/xslice_benchmark_test.go index 53b9505..56da493 100644 --- a/pkg/xslice/xslice_benchmark_test.go +++ b/pkg/xslice/xslice_benchmark_test.go @@ -2,6 +2,7 @@ package xslice_test import ( "bytes" + "fmt" "testing" "github.com/samber/lo" @@ -167,6 +168,21 @@ func BenchmarkSlice(b *testing.B) { } }) }) + + b.Run("benchmark map", func(b *testing.B) { + arr := _range(0, 1000) + + b.Run("xslice", func(b *testing.B) { + for i := 0; i < b.N; i++ { + xslice.Map(arr, func(x int) string { return fmt.Sprintf("%d", x) }) + } + }) + b.Run("lo", func(b *testing.B) { + for i := 0; i < b.N; i++ { + lo.Map(arr, func(x int, index int) string { return fmt.Sprintf("%d", x) }) + } + }) + }) } func BenchmarkChunk(b *testing.B) { From 78d77e7b90acbe30abd23c5b4364153acf9f3793 Mon Sep 17 00:00:00 2001 From: dashjay Date: Thu, 24 Jul 2025 01:39:30 +0800 Subject: [PATCH 2/8] implement --- pkg/internal/utils/utils.go | 6 +++++ pkg/xiter/xiter.go | 49 +++++++++++++++++++++++++++++++++++++ pkg/xiter/xiter_old.go | 42 +++++++++++++++++++++++++++++++ pkg/xiter/xiter_test.go | 27 ++++++++++++++++++++ pkg/xslice/xslice.go | 9 +++++++ pkg/xslice/xslice_test.go | 4 +++ 6 files changed, 137 insertions(+) create mode 100644 pkg/internal/utils/utils.go diff --git a/pkg/internal/utils/utils.go b/pkg/internal/utils/utils.go new file mode 100644 index 0000000..85ff544 --- /dev/null +++ b/pkg/internal/utils/utils.go @@ -0,0 +1,6 @@ +package utils + +func IsZero[T comparable](v T) bool { + var zero T + return v == zero +} diff --git a/pkg/xiter/xiter.go b/pkg/xiter/xiter.go index e45a880..dd33d29 100644 --- a/pkg/xiter/xiter.go +++ b/pkg/xiter/xiter.go @@ -9,6 +9,8 @@ import ( "math/rand" "strings" + "github.com/dashjay/xiter/pkg/internal/utils" + "github.com/dashjay/xiter/pkg/cmp" "github.com/dashjay/xiter/pkg/internal/constraints" "github.com/dashjay/xiter/pkg/optional" @@ -1030,3 +1032,50 @@ func MapToSeq2Value[T any, K comparable, V any](in Seq[T], mapFn func(ele T) (K, } } } + +func First[T any](in Seq[T]) (T, bool) { + var v T + var ok = false + for t := range in { + v = t + ok = true + break + } + return v, ok +} + +func FirstO[T any](in Seq[T]) optional.O[T] { + return optional.FromValue2(First(in)) +} + +func Last[T any](in Seq[T]) (T, bool) { + var v T + var ok = false + for t := range in { + v = t + ok = true + } + return v, ok +} + +func LastO[T any](in Seq[T]) optional.O[T] { + return optional.FromValue2(Last(in)) +} + +// Compact returns a new sequence with the zero elements removed. +// +// EXAMPLE: +// +// Compact([]int{0, 1, 2, 3, 4}) 👉 [1 2 3 4] +func Compact[T comparable](in Seq[T]) Seq[T] { + return func(yield func(T) bool) { + for t := range in { + if utils.IsZero(t) { + continue + } + if !yield(t) { + break + } + } + } +} diff --git a/pkg/xiter/xiter_old.go b/pkg/xiter/xiter_old.go index bb5866d..0460c60 100644 --- a/pkg/xiter/xiter_old.go +++ b/pkg/xiter/xiter_old.go @@ -11,6 +11,7 @@ import ( "github.com/dashjay/xiter/pkg/cmp" "github.com/dashjay/xiter/pkg/internal/constraints" + "github.com/dashjay/xiter/pkg/internal/utils" "github.com/dashjay/xiter/pkg/optional" "github.com/dashjay/xiter/pkg/union" ) @@ -931,3 +932,44 @@ func MapToSeq2Value[T any, K comparable, V any](in Seq[T], mapFn func(T) (K, V)) }) } } + +func First[T any](in Seq[T]) (T, bool) { + var v T + var ok = false + in(func(t T) bool { + v = t + ok = true + return false + }) + return v, ok +} + +func FirstO[T any](in Seq[T]) optional.O[T] { + return optional.FromValue2(First(in)) +} + +func Last[T any](in Seq[T]) (T, bool) { + var v T + var ok = false + in(func(t T) bool { + v = t + ok = true + return true + }) + return v, ok +} + +func LastO[T any](in Seq[T]) optional.O[T] { + return optional.FromValue2(Last(in)) +} + +func Compact[T comparable](in Seq[T]) Seq[T] { + return func(yield func(T) bool) { + in(func(t T) bool { + if utils.IsZero(t) { + return true + } + return yield(t) + }) + } +} diff --git a/pkg/xiter/xiter_test.go b/pkg/xiter/xiter_test.go index f83deb3..288b02d 100644 --- a/pkg/xiter/xiter_test.go +++ b/pkg/xiter/xiter_test.go @@ -391,6 +391,33 @@ func TestXIter(t *testing.T) { testLimit2(t, seq2, 1) assert.Equal(t, 9, xiter.ToMap(seq2)["3"]) }) + + t.Run("first", func(t *testing.T) { + val, ok := xiter.First(xiter.FromSlice([]int{0, 1, 2, 3, 4})) + assert.True(t, ok) + assert.Equal(t, 0, val) + assert.Equal(t, 0, xiter.FirstO(xiter.FromSlice([]int{0, 1, 2, 3, 4})).Must()) + + val, ok = xiter.First(xiter.FromSlice([]int{})) + assert.False(t, ok) + assert.False(t, xiter.FirstO(xiter.FromSlice([]int{})).Ok()) + }) + + t.Run("last", func(t *testing.T) { + val, ok := xiter.Last(xiter.FromSlice([]int{0, 1, 2, 3, 4})) + assert.True(t, ok) + assert.Equal(t, 4, val) + assert.Equal(t, 4, xiter.LastO(xiter.FromSlice([]int{0, 1, 2, 3, 4})).Must()) + + val, ok = xiter.Last(xiter.FromSlice([]int{})) + assert.False(t, ok) + assert.False(t, xiter.LastO(xiter.FromSlice([]int{})).Ok()) + }) + + t.Run("compact", func(t *testing.T) { + seq := xiter.Compact(xiter.FromSlice([]int{0, 1, 2, 3, 4})) + assert.Equal(t, []int{1, 2, 3, 4}, xiter.ToSlice(seq)) + }) } func TestXIter61898(t *testing.T) { diff --git a/pkg/xslice/xslice.go b/pkg/xslice/xslice.go index f1d0acf..0907340 100644 --- a/pkg/xslice/xslice.go +++ b/pkg/xslice/xslice.go @@ -576,3 +576,12 @@ func GroupByMap[T any, Slice ~[]T, K comparable, V any](in Slice, f func(T) (K, func Filter[T any, Slice ~[]T](in Slice, f func(T) bool) Slice { return xiter.ToSlice(xiter.Filter(f, xiter.FromSlice(in))) } + +// Compact returns a new slice with the zero elements removed. +// +// EXAMPLE: +// +// xslice.Compact([]int{0, 1, 2, 3, 4}) 👉 [1 2 3 4] +func Compact[T comparable, Slice ~[]T](in Slice) Slice { + return xiter.ToSlice(xiter.Compact(xiter.FromSlice(in))) +} diff --git a/pkg/xslice/xslice_test.go b/pkg/xslice/xslice_test.go index 28578c6..7d3dc8d 100644 --- a/pkg/xslice/xslice_test.go +++ b/pkg/xslice/xslice_test.go @@ -324,4 +324,8 @@ func TestSlices(t *testing.T) { t.Run("filter", func(t *testing.T) { assert.Equal(t, []int{2, 4}, xslice.Filter([]int{1, 2, 3, 4}, func(x int) bool { return x%2 == 0 })) }) + + t.Run("compact", func(t *testing.T) { + assert.Equal(t, []int{1, 2, 3, 4}, xslice.Compact([]int{0, 1, 2, 3, 4})) + }) } From 9eaceb20e35d7a528e5d96880eff185cd2cb86dd Mon Sep 17 00:00:00 2001 From: dashjay Date: Thu, 24 Jul 2025 01:41:16 +0800 Subject: [PATCH 3/8] update doc --- pkg/internal/README.md | 20 +++++ pkg/xiter/README.md | 186 +++++++++++++++++++++++++++-------------- pkg/xslice/README.md | 16 ++++ 3 files changed, 157 insertions(+), 65 deletions(-) diff --git a/pkg/internal/README.md b/pkg/internal/README.md index 00d0189..d09d61e 100644 --- a/pkg/internal/README.md +++ b/pkg/internal/README.md @@ -60,6 +60,26 @@ type Ordered interface { } ``` +# utils + +```go +import "github.com/dashjay/xiter/pkg/internal/utils" +``` + +## Index + +- [func IsZero\[T comparable\]\(v T\) bool](<#IsZero>) + + + +## func [IsZero]() + +```go +func IsZero[T comparable](v T) bool +``` + + + # xassert ```go diff --git a/pkg/xiter/README.md b/pkg/xiter/README.md index 9d7ee33..4bcb5fb 100644 --- a/pkg/xiter/README.md +++ b/pkg/xiter/README.md @@ -28,12 +28,16 @@ WARNING: golang 1.23 has higher performance on iterating Seq/Seq2 which boost by - [func EqualFunc2\[K1, V1, K2, V2 any\]\(x Seq2\[K1, V1\], y Seq2\[K2, V2\], f func\(K1, V1, K2, V2\) bool\) bool](<#EqualFunc2>) - [func Find\[T any\]\(seq Seq\[T\], f func\(T\) bool\) \(val T, found bool\)](<#Find>) - [func FindO\[T any\]\(seq Seq\[T\], f func\(T\) bool\) optional.O\[T\]](<#FindO>) +- [func First\[T any\]\(in Seq\[T\]\) \(T, bool\)](<#First>) +- [func FirstO\[T any\]\(in Seq\[T\]\) optional.O\[T\]](<#FirstO>) - [func ForEach\[T any\]\(seq Seq\[T\], f func\(T\) bool\)](<#ForEach>) - [func ForEachIdx\[T any\]\(seq Seq\[T\], f func\(idx int, v T\) bool\)](<#ForEachIdx>) - [func Head\[T any\]\(seq Seq\[T\]\) \(v T, hasOne bool\)](<#Head>) - [func HeadO\[T any\]\(seq Seq\[T\]\) optional.O\[T\]](<#HeadO>) - [func Index\[T comparable\]\(seq Seq\[T\], v T\) int](<#Index>) - [func Join\[T \~string\]\(seq Seq\[T\], sep T\) T](<#Join>) +- [func Last\[T any\]\(in Seq\[T\]\) \(T, bool\)](<#Last>) +- [func LastO\[T any\]\(in Seq\[T\]\) optional.O\[T\]](<#LastO>) - [func Max\[T constraints.Ordered\]\(seq Seq\[T\]\) \(r optional.O\[T\]\)](<#Max>) - [func MaxBy\[T constraints.Ordered\]\(seq Seq\[T\], less func\(T, T\) bool\) \(r optional.O\[T\]\)](<#MaxBy>) - [func Min\[T constraints.Ordered\]\(seq Seq\[T\]\) \(r optional.O\[T\]\)](<#Min>) @@ -50,6 +54,7 @@ WARNING: golang 1.23 has higher performance on iterating Seq/Seq2 which boost by - [func ToSliceSeq2Value\[K, V any\]\(seq Seq2\[K, V\]\) \(out \[\]V\)](<#ToSliceSeq2Value>) - [type Seq](<#Seq>) - [func Chunk\[T any\]\(seq Seq\[T\], n int\) Seq\[\[\]T\]](<#Chunk>) + - [func Compact\[T comparable\]\(in Seq\[T\]\) Seq\[T\]](<#Compact>) - [func Concat\[V any\]\(seqs ...Seq\[V\]\) Seq\[V\]](<#Concat>) - [func Filter\[V any\]\(f func\(V\) bool, seq Seq\[V\]\) Seq\[V\]](<#Filter>) - [func FromChan\[T any\]\(in \<\-chan T\) Seq\[T\]](<#FromChan>) @@ -89,7 +94,7 @@ WARNING: golang 1.23 has higher performance on iterating Seq/Seq2 which boost by -## func [AllFromSeq]() +## func [AllFromSeq]() ```go func AllFromSeq[T any](seq Seq[T], f func(T) bool) bool @@ -98,7 +103,7 @@ func AllFromSeq[T any](seq Seq[T], f func(T) bool) bool AllFromSeq return true if all elements from seq satisfy the condition evaluated by f. -## func [AnyFromSeq]() +## func [AnyFromSeq]() ```go func AnyFromSeq[T any](seq Seq[T], f func(T) bool) bool @@ -116,7 +121,7 @@ func At[T any](seq Seq[T], index int) optional.O[T] At return the element at index from seq. -## func [AvgByFromSeq]() +## func [AvgByFromSeq]() ```go func AvgByFromSeq[V any, T constraints.Number](seq Seq[V], f func(V) T) float64 @@ -125,7 +130,7 @@ func AvgByFromSeq[V any, T constraints.Number](seq Seq[V], f func(V) T) float64 AvgByFromSeq return the average value of all elements from seq, evaluated by f. -## func [AvgFromSeq]() +## func [AvgFromSeq]() ```go func AvgFromSeq[T constraints.Number](seq Seq[T]) float64 @@ -134,7 +139,7 @@ func AvgFromSeq[T constraints.Number](seq Seq[T]) float64 AvgFromSeq return the average value of all elements from seq. -## func [Contains]() +## func [Contains]() ```go func Contains[T comparable](seq Seq[T], in T) bool @@ -143,7 +148,7 @@ func Contains[T comparable](seq Seq[T], in T) bool Contains return true if v is in seq. -## func [ContainsAll]() +## func [ContainsAll]() ```go func ContainsAll[T comparable](seq Seq[T], in []T) bool @@ -152,7 +157,7 @@ func ContainsAll[T comparable](seq Seq[T], in []T) bool ContainsAll return true if all elements from seq is in vs. -## func [ContainsAny]() +## func [ContainsAny]() ```go func ContainsAny[T comparable](seq Seq[T], in []T) bool @@ -161,7 +166,7 @@ func ContainsAny[T comparable](seq Seq[T], in []T) bool ContainsAny return true if any element from seq is in vs. -## func [ContainsBy]() +## func [ContainsBy]() ```go func ContainsBy[T any](seq Seq[T], f func(T) bool) bool @@ -170,7 +175,7 @@ func ContainsBy[T any](seq Seq[T], f func(T) bool) bool ContainsBy return true if any element from seq satisfies the condition evaluated by f. -## func [Count]() +## func [Count]() ```go func Count[T any](seq Seq[T]) int @@ -179,7 +184,7 @@ func Count[T any](seq Seq[T]) int Count return the number of elements in seq. -## func [Equal]() +## func [Equal]() ```go func Equal[V comparable](x, y Seq[V]) bool @@ -234,7 +239,7 @@ false -## func [Equal2]() +## func [Equal2]() ```go func Equal2[K, V comparable](x, y Seq2[K, V]) bool @@ -243,7 +248,7 @@ func Equal2[K, V comparable](x, y Seq2[K, V]) bool Equal2 returns whether the two Seq2 are equal. Like Equal but run with Seq2 -## func [EqualFunc]() +## func [EqualFunc]() ```go func EqualFunc[V1, V2 any](x Seq[V1], y Seq[V2], f func(V1, V2) bool) bool @@ -303,7 +308,7 @@ false -## func [EqualFunc2]() +## func [EqualFunc2]() ```go func EqualFunc2[K1, V1, K2, V2 any](x Seq2[K1, V1], y Seq2[K2, V2], f func(K1, V1, K2, V2) bool) bool @@ -312,7 +317,7 @@ func EqualFunc2[K1, V1, K2, V2 any](x Seq2[K1, V1], y Seq2[K2, V2], f func(K1, V EqualFunc2 returns whether the two sequences are equal according to the function f. Like EqualFunc but run with Seq2 -## func [Find]() +## func [Find]() ```go func Find[T any](seq Seq[T], f func(T) bool) (val T, found bool) @@ -321,7 +326,7 @@ func Find[T any](seq Seq[T], f func(T) bool) (val T, found bool) Find return the first element from seq that satisfies the condition evaluated by f with a boolean representing whether it exists. -## func [FindO]() +## func [FindO]() ```go func FindO[T any](seq Seq[T], f func(T) bool) optional.O[T] @@ -329,8 +334,26 @@ func FindO[T any](seq Seq[T], f func(T) bool) optional.O[T] FindO return the first element from seq that satisfies the condition evaluated by f. + +## func [First]() + +```go +func First[T any](in Seq[T]) (T, bool) +``` + + + + +## func [FirstO]() + +```go +func FirstO[T any](in Seq[T]) optional.O[T] +``` + + + -## func [ForEach]() +## func [ForEach]() ```go func ForEach[T any](seq Seq[T], f func(T) bool) @@ -339,7 +362,7 @@ func ForEach[T any](seq Seq[T], f func(T) bool) ForEach execute f for each element in seq. -## func [ForEachIdx]() +## func [ForEachIdx]() ```go func ForEachIdx[T any](seq Seq[T], f func(idx int, v T) bool) @@ -348,7 +371,7 @@ func ForEachIdx[T any](seq Seq[T], f func(idx int, v T) bool) ForEachIdx execute f for each element in seq with its index. -## func [Head]() +## func [Head]() ```go func Head[T any](seq Seq[T]) (v T, hasOne bool) @@ -357,7 +380,7 @@ func Head[T any](seq Seq[T]) (v T, hasOne bool) Head return the first element from seq with a boolean representing whether it is at least one element in seq. -## func [HeadO]() +## func [HeadO]() ```go func HeadO[T any](seq Seq[T]) optional.O[T] @@ -366,7 +389,7 @@ func HeadO[T any](seq Seq[T]) optional.O[T] HeadO return the first element from seq. -## func [Index]() +## func [Index]() ```go func Index[T comparable](seq Seq[T], v T) int @@ -383,7 +406,7 @@ idx := xiter.Index(seq, 3) ``` -## func [Join]() +## func [Join]() ```go func Join[T ~string](seq Seq[T], sep T) T @@ -391,8 +414,26 @@ func Join[T ~string](seq Seq[T], sep T) T Join return the concatenation of all elements in seq with sep. + +## func [Last]() + +```go +func Last[T any](in Seq[T]) (T, bool) +``` + + + + +## func [LastO]() + +```go +func LastO[T any](in Seq[T]) optional.O[T] +``` + + + -## func [Max]() +## func [Max]() ```go func Max[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) @@ -401,7 +442,7 @@ func Max[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) Max returns the maximum element in seq. -## func [MaxBy]() +## func [MaxBy]() ```go func MaxBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional.O[T]) @@ -410,7 +451,7 @@ func MaxBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional. MaxBy return the maximum element in seq, evaluated by f. -## func [Min]() +## func [Min]() ```go func Min[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) @@ -419,7 +460,7 @@ func Min[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) Min return the minimum element in seq. -## func [MinBy]() +## func [MinBy]() ```go func MinBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional.O[T]) @@ -428,7 +469,7 @@ func MinBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional. MinBy return the minimum element in seq, evaluated by f. -## func [Pull]() +## func [Pull]() ```go func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()) @@ -479,7 +520,7 @@ func main() { -## func [Pull2]() +## func [Pull2]() ```go func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func()) @@ -488,7 +529,7 @@ func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func()) -## func [Reduce]() +## func [Reduce]() ```go func Reduce[Sum, V any](f func(Sum, V) Sum, sum Sum, seq Seq[V]) Sum @@ -547,7 +588,7 @@ func main() { -## func [Reduce2]() +## func [Reduce2]() ```go func Reduce2[Sum, K, V any](f func(Sum, K, V) Sum, sum Sum, seq Seq2[K, V]) Sum @@ -556,7 +597,7 @@ func Reduce2[Sum, K, V any](f func(Sum, K, V) Sum, sum Sum, seq Seq2[K, V]) Sum Reduce2 combines the values in seq using f. For each pair k, v in seq, it updates sum = f\(sum, k, v\) and then returns the final sum. For example, if iterating over seq yields \(k1, v1\), \(k2, v2\), \(k3, v3\) Reduce returns f\(f\(f\(sum, k1, v1\), k2, v2\), k3, v3\). -## func [Sum]() +## func [Sum]() ```go func Sum[T constraints.Number](seq Seq[T]) T @@ -573,7 +614,7 @@ sum := xiter.Sum(seq) ``` -## func [ToMap]() +## func [ToMap]() ```go func ToMap[K comparable, V any](seq Seq2[K, V]) (out map[K]V) @@ -582,7 +623,7 @@ func ToMap[K comparable, V any](seq Seq2[K, V]) (out map[K]V) -## func [ToSlice]() +## func [ToSlice]() ```go func ToSlice[T any](seq Seq[T]) (out []T) @@ -591,7 +632,7 @@ func ToSlice[T any](seq Seq[T]) (out []T) ToSlice returns the elements in seq as a slice. -## func [ToSliceN]() +## func [ToSliceN]() ```go func ToSliceN[T any](seq Seq[T], n int) (out []T) @@ -600,7 +641,7 @@ func ToSliceN[T any](seq Seq[T], n int) (out []T) ToSliceN pull out n elements from seq. -## func [ToSliceSeq2Key]() +## func [ToSliceSeq2Key]() ```go func ToSliceSeq2Key[K, V any](seq Seq2[K, V]) (out []K) @@ -617,7 +658,7 @@ keys := ToSliceSeq2Key(seq) ``` -## func [ToSliceSeq2Value]() +## func [ToSliceSeq2Value]() ```go func ToSliceSeq2Value[K, V any](seq Seq2[K, V]) (out []V) @@ -634,7 +675,7 @@ values := ToSliceSeq2Value(seq) ``` -## type [Seq]() +## type [Seq]() Seq is a sequence of elements provided by an iterator\-like function. We made this alias Seq to iter.Seq for providing a compatible interface in lower go versions. @@ -643,7 +684,7 @@ type Seq[V any] iter.Seq[V] ``` -### func [Chunk]() +### func [Chunk]() ```go func Chunk[T any](seq Seq[T], n int) Seq[[]T] @@ -659,8 +700,23 @@ chunkedSeq := xiter.Chunk(seq, 2) // xiter.ToSlice(chunkedSeq) returns [][]int{{1,2}, {3,4}, {5}} ``` + +### func [Compact]() + +```go +func Compact[T comparable](in Seq[T]) Seq[T] +``` + +Compact returns a new sequence with the zero elements removed. + +EXAMPLE: + +``` +Compact([]int{0, 1, 2, 3, 4}) 👉 [1 2 3 4] +``` + -### func [Concat]() +### func [Concat]() ```go func Concat[V any](seqs ...Seq[V]) Seq[V] @@ -713,7 +769,7 @@ func main() { -### func [Filter]() +### func [Filter]() ```go func Filter[V any](f func(V) bool, seq Seq[V]) Seq[V] @@ -783,7 +839,7 @@ _ = ToSlice(seq) // Returns []int{1, 2} ``` -### func [FromMapKeys]() +### func [FromMapKeys]() ```go func FromMapKeys[K comparable, V any](m map[K]V) Seq[K] @@ -792,7 +848,7 @@ func FromMapKeys[K comparable, V any](m map[K]V) Seq[K] -### func [FromMapValues]() +### func [FromMapValues]() ```go func FromMapValues[K comparable, V any](m map[K]V) Seq[V] @@ -819,7 +875,7 @@ func FromSliceReverse[T any, Slice ~[]T](in Slice) Seq[T] -### func [FromSliceShuffle]() +### func [FromSliceShuffle]() ```go func FromSliceShuffle[T any](in []T) Seq[T] @@ -836,7 +892,7 @@ shuffledSeq := FromSliceShuffle(ToSlice(seq)) ``` -### func [Limit]() +### func [Limit]() ```go func Limit[V any](seq Seq[V], n int) Seq[V] @@ -885,7 +941,7 @@ func main() { -### func [Map]() +### func [Map]() ```go func Map[In, Out any](f func(In) Out, seq Seq[In]) Seq[Out] @@ -934,7 +990,7 @@ func main() { -### func [Merge]() +### func [Merge]() ```go func Merge[V cmp.Ordered](x, y Seq[V]) Seq[V] @@ -976,7 +1032,7 @@ func main() { -### func [MergeFunc]() +### func [MergeFunc]() ```go func MergeFunc[V any](x, y Seq[V], f func(V, V) int) Seq[V] @@ -994,7 +1050,7 @@ func Repeat[T any](seq Seq[T], count int) Seq[T] Repeat return a seq that repeat seq for count times. -### func [Replace]() +### func [Replace]() ```go func Replace[T comparable](seq Seq[T], from, to T, n int) Seq[T] @@ -1011,7 +1067,7 @@ replacedSeq := Replace(seq, 2, 99, -1) // Replace all 2s with 99 ``` -### func [ReplaceAll]() +### func [ReplaceAll]() ```go func ReplaceAll[T comparable](seq Seq[T], from, to T) Seq[T] @@ -1037,7 +1093,7 @@ func Reverse[T any](seq Seq[T]) Seq[T] Reverse return a reversed seq. -### func [Seq2KeyToSeq]() +### func [Seq2KeyToSeq]() ```go func Seq2KeyToSeq[K, V any](in Seq2[K, V]) Seq[K] @@ -1046,7 +1102,7 @@ func Seq2KeyToSeq[K, V any](in Seq2[K, V]) Seq[K] -### func [Seq2ToSeqUnion]() +### func [Seq2ToSeqUnion]() ```go func Seq2ToSeqUnion[K, V any](seq Seq2[K, V]) Seq[union.U2[K, V]] @@ -1064,7 +1120,7 @@ for v := range Seq2ToSeqUnion(seq2) { ``` -### func [Seq2ValueToSeq]() +### func [Seq2ValueToSeq]() ```go func Seq2ValueToSeq[K, V any](in Seq2[K, V]) Seq[V] @@ -1073,7 +1129,7 @@ func Seq2ValueToSeq[K, V any](in Seq2[K, V]) Seq[V] -### func [Skip]() +### func [Skip]() ```go func Skip[T any](seq Seq[T], n int) Seq[T] @@ -1082,7 +1138,7 @@ func Skip[T any](seq Seq[T], n int) Seq[T] Skip return a seq that skip n elements from seq. -### func [Uniq]() +### func [Uniq]() ```go func Uniq[T comparable](seq Seq[T]) Seq[T] @@ -1099,7 +1155,7 @@ uniqSeq := xiter.Uniq(seq) ``` -### func [Zip]() +### func [Zip]() ```go func Zip[V1, V2 any](x Seq[V1], y Seq[V2]) Seq[Zipped[V1, V2]] @@ -1157,7 +1213,7 @@ func main() { -### func [Zip2]() +### func [Zip2]() ```go func Zip2[K1, V1, K2, V2 any](x Seq2[K1, V1], y Seq2[K2, V2]) Seq[Zipped2[K1, V1, K2, V2]] @@ -1179,7 +1235,7 @@ func Equal2[K, V comparable](x, y Seq2[K, V]) bool { ``` -## type [Seq2]() +## type [Seq2]() Seq2 is a sequence of key/value pair provided by an iterator\-like function. We made this alias Seq2 to iter.Seq2 for providing a compatible interface in lower go versions. @@ -1188,7 +1244,7 @@ type Seq2[K, V any] iter.Seq2[K, V] ``` -### func [Concat2]() +### func [Concat2]() ```go func Concat2[K, V any](seqs ...Seq2[K, V]) Seq2[K, V] @@ -1197,7 +1253,7 @@ func Concat2[K, V any](seqs ...Seq2[K, V]) Seq2[K, V] Concat2 returns an Seq2 over the concatenation of the given Seq2s. Like Concat but run with Seq2 -### func [Filter2]() +### func [Filter2]() ```go func Filter2[K, V any](f func(K, V) bool, seq Seq2[K, V]) Seq2[K, V] @@ -1206,7 +1262,7 @@ func Filter2[K, V any](f func(K, V) bool, seq Seq2[K, V]) Seq2[K, V] Filter2 returns an Seq over seq that only includes the key\-value pairs k, v for which f\(k, v\) is true. Like Filter but run with Seq2 -### func [FromMapKeyAndValues]() +### func [FromMapKeyAndValues]() ```go func FromMapKeyAndValues[K comparable, V any](m map[K]V) Seq2[K, V] @@ -1224,7 +1280,7 @@ func FromSliceIdx[T any](in []T) Seq2[int, T] FromSliceIdx received a slice and returned a Seq2 for this slice, key is index. -### func [Limit2]() +### func [Limit2]() ```go func Limit2[K, V any](seq Seq2[K, V], n int) Seq2[K, V] @@ -1233,7 +1289,7 @@ func Limit2[K, V any](seq Seq2[K, V], n int) Seq2[K, V] Limit2 returns a Seq over Seq2 that stops after n key\-value pairs. Like Limit but run with Seq2 -### func [Map2]() +### func [Map2]() ```go func Map2[KIn, VIn, KOut, VOut any](f func(KIn, VIn) (KOut, VOut), seq Seq2[KIn, VIn]) Seq2[KOut, VOut] @@ -1242,7 +1298,7 @@ func Map2[KIn, VIn, KOut, VOut any](f func(KIn, VIn) (KOut, VOut), seq Seq2[KIn, Map2 returns a Seq2 over the results of applying f to each key\-value pair in seq. Like Map but run with Seq2 -### func [MapToSeq2]() +### func [MapToSeq2]() ```go func MapToSeq2[T any, K comparable](in Seq[T], mapFn func(ele T) K) Seq2[K, T] @@ -1263,7 +1319,7 @@ fmt.Println(ToMap(lenMap)) ``` -### func [MapToSeq2Value]() +### func [MapToSeq2Value]() ```go func MapToSeq2Value[T any, K comparable, V any](in Seq[T], mapFn func(ele T) (K, V)) Seq2[K, V] @@ -1283,7 +1339,7 @@ fmt.Println(ToMap(transformed)) ``` -### func [Merge2]() +### func [Merge2]() ```go func Merge2[K cmp.Ordered, V any](x, y Seq2[K, V]) Seq2[K, V] @@ -1294,7 +1350,7 @@ Merge2 merges two sequences of key\-value pairs ordered by their keys. Pairs app Merge2 is equivalent to calling MergeFunc2 with cmp.Compare\[K\] as the ordering function. -### func [MergeFunc2]() +### func [MergeFunc2]() ```go func MergeFunc2[K, V any](x, y Seq2[K, V], f func(K, K) int) Seq2[K, V] diff --git a/pkg/xslice/README.md b/pkg/xslice/README.md index bc866e2..a7d3202 100644 --- a/pkg/xslice/README.md +++ b/pkg/xslice/README.md @@ -17,6 +17,7 @@ import "github.com/dashjay/xiter/pkg/xslice" - [func ChunkInPlace\[T any, Slice \~\[\]T\]\(in Slice, chunkSize int\) \[\]Slice](<#ChunkInPlace>) - [func Clone\[T any\]\(in \[\]T\) \[\]T](<#Clone>) - [func CloneBy\[T any, U any\]\(in \[\]T, f func\(T\) U\) \[\]U](<#CloneBy>) +- [func Compact\[T comparable, Slice \~\[\]T\]\(in Slice\) Slice](<#Compact>) - [func Concat\[T any\]\(vs ...\[\]T\) \[\]T](<#Concat>) - [func Contains\[T comparable\]\(in \[\]T, v T\) bool](<#Contains>) - [func ContainsAll\[T comparable\]\(in \[\]T, v \[\]T\) bool](<#ContainsAll>) @@ -202,6 +203,21 @@ xslice.CloneBy([]int{1, 2, 3}, func(x int) int { return x * 2 }) 👉 [2, 4, 6] xslice.CloneBy([]int{1, 2, 3}, strconv.Itoa) 👉 ["1", "2", "3"] ``` + +## func [Compact]() + +```go +func Compact[T comparable, Slice ~[]T](in Slice) Slice +``` + +Compact returns a new slice with the zero elements removed. + +EXAMPLE: + +``` +xslice.Compact([]int{0, 1, 2, 3, 4}) 👉 [1 2 3 4] +``` + ## func [Concat]() From cc2bc8358ef9fdf1eb1ef9f71bf26936f36c0756 Mon Sep 17 00:00:00 2001 From: dashjay Date: Thu, 24 Jul 2025 01:45:10 +0800 Subject: [PATCH 4/8] test first and last --- pkg/xiter/README.md | 34 ++++++++++++++++++---- pkg/xiter/xiter.go | 28 ++++++++++++++++++ pkg/xslice/README.md | 60 +++++++++++++++++++++++++++++++++++++++ pkg/xslice/xslice.go | 40 ++++++++++++++++++++++++++ pkg/xslice/xslice_test.go | 8 ++++++ 5 files changed, 165 insertions(+), 5 deletions(-) diff --git a/pkg/xiter/README.md b/pkg/xiter/README.md index 4bcb5fb..7b2b70e 100644 --- a/pkg/xiter/README.md +++ b/pkg/xiter/README.md @@ -335,22 +335,34 @@ func FindO[T any](seq Seq[T], f func(T) bool) optional.O[T] FindO return the first element from seq that satisfies the condition evaluated by f. -## func [First]() +## func [First]() ```go func First[T any](in Seq[T]) (T, bool) ``` +First returns the first element in the sequence. If the sequence is empty, the zero value of T is returned. Example: +``` +seq := FromSlice([]int{1, 2, 3}) +first, ok := First(seq) +// first is 1, ok is true +``` -## func [FirstO]() +## func [FirstO]() ```go func FirstO[T any](in Seq[T]) optional.O[T] ``` +FirstO returns the first element in the sequence as an optional.O\[T\]. If the sequence is empty, the zero value of T is returned. Example: +``` +seq := FromSlice([]int{1, 2, 3}) +first, ok := FirstO(seq) +// first is 1, ok is true +``` ## func [ForEach]() @@ -415,22 +427,34 @@ func Join[T ~string](seq Seq[T], sep T) T Join return the concatenation of all elements in seq with sep. -## func [Last]() +## func [Last]() ```go func Last[T any](in Seq[T]) (T, bool) ``` +Last returns the last element in the sequence. If the sequence is empty, the zero value of T is returned. Example: +``` +seq := FromSlice([]int{1, 2, 3}) +last, ok := Last(seq) +// last is 3, ok is true +``` -## func [LastO]() +## func [LastO]() ```go func LastO[T any](in Seq[T]) optional.O[T] ``` +LastO returns the last element in the sequence as an optional.O\[T\]. If the sequence is empty, the zero value of T is returned. Example: +``` +seq := FromSlice([]int{1, 2, 3}) +last, ok := LastO(seq) +// last is 3, ok is true +``` ## func [Max]() @@ -701,7 +725,7 @@ chunkedSeq := xiter.Chunk(seq, 2) ``` -### func [Compact]() +### func [Compact]() ```go func Compact[T comparable](in Seq[T]) Seq[T] diff --git a/pkg/xiter/xiter.go b/pkg/xiter/xiter.go index dd33d29..9fb6c38 100644 --- a/pkg/xiter/xiter.go +++ b/pkg/xiter/xiter.go @@ -1033,6 +1033,13 @@ func MapToSeq2Value[T any, K comparable, V any](in Seq[T], mapFn func(ele T) (K, } } +// First returns the first element in the sequence. +// If the sequence is empty, the zero value of T is returned. +// Example: +// +// seq := FromSlice([]int{1, 2, 3}) +// first, ok := First(seq) +// // first is 1, ok is true func First[T any](in Seq[T]) (T, bool) { var v T var ok = false @@ -1044,10 +1051,24 @@ func First[T any](in Seq[T]) (T, bool) { return v, ok } +// FirstO returns the first element in the sequence as an optional.O[T]. +// If the sequence is empty, the zero value of T is returned. +// Example: +// +// seq := FromSlice([]int{1, 2, 3}) +// first, ok := FirstO(seq) +// // first is 1, ok is true func FirstO[T any](in Seq[T]) optional.O[T] { return optional.FromValue2(First(in)) } +// Last returns the last element in the sequence. +// If the sequence is empty, the zero value of T is returned. +// Example: +// +// seq := FromSlice([]int{1, 2, 3}) +// last, ok := Last(seq) +// // last is 3, ok is true func Last[T any](in Seq[T]) (T, bool) { var v T var ok = false @@ -1058,6 +1079,13 @@ func Last[T any](in Seq[T]) (T, bool) { return v, ok } +// LastO returns the last element in the sequence as an optional.O[T]. +// If the sequence is empty, the zero value of T is returned. +// Example: +// +// seq := FromSlice([]int{1, 2, 3}) +// last, ok := LastO(seq) +// // last is 3, ok is true func LastO[T any](in Seq[T]) optional.O[T] { return optional.FromValue2(Last(in)) } diff --git a/pkg/xslice/README.md b/pkg/xslice/README.md index a7d3202..35c5417 100644 --- a/pkg/xslice/README.md +++ b/pkg/xslice/README.md @@ -27,6 +27,8 @@ import "github.com/dashjay/xiter/pkg/xslice" - [func Filter\[T any, Slice \~\[\]T\]\(in Slice, f func\(T\) bool\) Slice](<#Filter>) - [func Find\[T any\]\(in \[\]T, f func\(T\) bool\) \(val T, found bool\)](<#Find>) - [func FindO\[T any\]\(in \[\]T, f func\(T\) bool\) optional.O\[T\]](<#FindO>) +- [func First\[T any, Slice \~\[\]T\]\(in Slice\) \(T, bool\)](<#First>) +- [func FirstO\[T any, Slice \~\[\]T\]\(in Slice\) optional.O\[T\]](<#FirstO>) - [func ForEach\[T any\]\(in \[\]T, f func\(T\) bool\)](<#ForEach>) - [func ForEachIdx\[T any\]\(in \[\]T, f func\(idx int, v T\) bool\)](<#ForEachIdx>) - [func GroupBy\[T any, K comparable, Slice \~\[\]T\]\(in Slice, f func\(T\) K\) map\[K\]Slice](<#GroupBy>) @@ -35,6 +37,8 @@ import "github.com/dashjay/xiter/pkg/xslice" - [func HeadO\[T any\]\(in \[\]T\) optional.O\[T\]](<#HeadO>) - [func Index\[T comparable, Slice \~\[\]T\]\(in Slice, v T\) int](<#Index>) - [func Join\[T \~string\]\(in \[\]T, sep T\) T](<#Join>) +- [func Last\[T any, Slice \~\[\]T\]\(in Slice\) \(T, bool\)](<#Last>) +- [func LastO\[T any, Slice \~\[\]T\]\(in Slice\) optional.O\[T\]](<#LastO>) - [func Map\[T any, U any\]\(in \[\]T, f func\(T\) U\) \[\]U](<#Map>) - [func Max\[T constraints.Ordered\]\(in \[\]T\) optional.O\[T\]](<#Max>) - [func MaxBy\[T constraints.Ordered\]\(in \[\]T, f func\(T, T\) bool\) optional.O\[T\]](<#MaxBy>) @@ -370,6 +374,34 @@ xslice.FindO(_range(0, 10), func(x int) bool { return x == 1 }).Must() 👉 1 xslice.FindO(_range(0, 10), func(x int) bool { return x == -1 }).Ok() 👉 false ``` + +## func [First]() + +```go +func First[T any, Slice ~[]T](in Slice) (T, bool) +``` + +First returns the first element in the slice. If the slice is empty, the zero value of T is returned. EXAMPLE: + +``` +xslice.First([]int{1, 2, 3}) 👉 1 +xslice.First([]int{}) 👉 0 +``` + + +## func [FirstO]() + +```go +func FirstO[T any, Slice ~[]T](in Slice) optional.O[T] +``` + +FirstO returns the first element in the slice as an optional.O\[T\]. If the slice is empty, the zero value of T is returned. EXAMPLE: + +``` +xslice.FirstO([]int{1, 2, 3}) 👉 1 +xslice.FirstO([]int{}) 👉 0 +``` + ## func [ForEach]() @@ -507,6 +539,34 @@ xslice.Join([]string{"1", "2", "3"}, ".") 👉 "1.2.3" xslice.Join([]string{}, ".") 👉 "" ``` + +## func [Last]() + +```go +func Last[T any, Slice ~[]T](in Slice) (T, bool) +``` + +Last returns the last element in the slice. If the slice is empty, the zero value of T is returned. EXAMPLE: + +``` +xslice.Last([]int{1, 2, 3}) 👉 3 +xslice.Last([]int{}) 👉 0 +``` + + +## func [LastO]() + +```go +func LastO[T any, Slice ~[]T](in Slice) optional.O[T] +``` + +LastO returns the last element in the slice as an optional.O\[T\]. If the slice is empty, the zero value of T is returned. EXAMPLE: + +``` +xslice.LastO([]int{1, 2, 3}) 👉 3 +xslice.LastO([]int{}) 👉 0 +``` + ## func [Map]() diff --git a/pkg/xslice/xslice.go b/pkg/xslice/xslice.go index 0907340..4029daf 100644 --- a/pkg/xslice/xslice.go +++ b/pkg/xslice/xslice.go @@ -585,3 +585,43 @@ func Filter[T any, Slice ~[]T](in Slice, f func(T) bool) Slice { func Compact[T comparable, Slice ~[]T](in Slice) Slice { return xiter.ToSlice(xiter.Compact(xiter.FromSlice(in))) } + +// First returns the first element in the slice. +// If the slice is empty, the zero value of T is returned. +// EXAMPLE: +// +// xslice.First([]int{1, 2, 3}) 👉 1 +// xslice.First([]int{}) 👉 0 +func First[T any, Slice ~[]T](in Slice) (T, bool) { + return xiter.First(xiter.FromSlice(in)) +} + +// FirstO returns the first element in the slice as an optional.O[T]. +// If the slice is empty, the zero value of T is returned. +// EXAMPLE: +// +// xslice.FirstO([]int{1, 2, 3}) 👉 1 +// xslice.FirstO([]int{}) 👉 0 +func FirstO[T any, Slice ~[]T](in Slice) optional.O[T] { + return optional.FromValue2(First(in)) +} + +// Last returns the last element in the slice. +// If the slice is empty, the zero value of T is returned. +// EXAMPLE: +// +// xslice.Last([]int{1, 2, 3}) 👉 3 +// xslice.Last([]int{}) 👉 0 +func Last[T any, Slice ~[]T](in Slice) (T, bool) { + return xiter.Last(xiter.FromSlice(in)) +} + +// LastO returns the last element in the slice as an optional.O[T]. +// If the slice is empty, the zero value of T is returned. +// EXAMPLE: +// +// xslice.LastO([]int{1, 2, 3}) 👉 3 +// xslice.LastO([]int{}) 👉 0 +func LastO[T any, Slice ~[]T](in Slice) optional.O[T] { + return optional.FromValue2(Last(in)) +} diff --git a/pkg/xslice/xslice_test.go b/pkg/xslice/xslice_test.go index 7d3dc8d..84e66e1 100644 --- a/pkg/xslice/xslice_test.go +++ b/pkg/xslice/xslice_test.go @@ -328,4 +328,12 @@ func TestSlices(t *testing.T) { t.Run("compact", func(t *testing.T) { assert.Equal(t, []int{1, 2, 3, 4}, xslice.Compact([]int{0, 1, 2, 3, 4})) }) + + t.Run("first", func(t *testing.T) { + assert.Equal(t, 1, xslice.FirstO([]int{1, 2, 3}).Must()) + }) + + t.Run("last", func(t *testing.T) { + assert.Equal(t, 3, xslice.LastO([]int{1, 2, 3}).Must()) + }) } From 4d92c423c9f77e27dd710bc9a10c2abce99ff8c9 Mon Sep 17 00:00:00 2001 From: dashjay Date: Thu, 24 Jul 2025 21:13:55 +0800 Subject: [PATCH 5/8] implement difference --- pkg/xiter/README.md | 114 ++++++++++++++++++---------- pkg/xiter/xiter.go | 8 ++ pkg/xiter/xiter_common.go | 28 +++++++ pkg/xiter/xiter_common_test.go | 11 ++- pkg/xiter/xiter_old.go | 9 +++ pkg/xslice/README.md | 20 +++++ pkg/xslice/xslice.go | 15 ++++ pkg/xslice/xslice_benchmark_test.go | 15 ++++ pkg/xslice/xslice_test.go | 11 +++ 9 files changed, 188 insertions(+), 43 deletions(-) diff --git a/pkg/xiter/README.md b/pkg/xiter/README.md index 7b2b70e..3f280f4 100644 --- a/pkg/xiter/README.md +++ b/pkg/xiter/README.md @@ -22,6 +22,7 @@ WARNING: golang 1.23 has higher performance on iterating Seq/Seq2 which boost by - [func ContainsAny\[T comparable\]\(seq Seq\[T\], in \[\]T\) bool](<#ContainsAny>) - [func ContainsBy\[T any\]\(seq Seq\[T\], f func\(T\) bool\) bool](<#ContainsBy>) - [func Count\[T any\]\(seq Seq\[T\]\) int](<#Count>) +- [func Difference\[T comparable\]\(left Seq\[T\], right Seq\[T\]\) \(onlyLeft Seq\[T\], onlyRight Seq\[T\]\)](<#Difference>) - [func Equal\[V comparable\]\(x, y Seq\[V\]\) bool](<#Equal>) - [func Equal2\[K, V comparable\]\(x, y Seq2\[K, V\]\) bool](<#Equal2>) - [func EqualFunc\[V1, V2 any\]\(x Seq\[V1\], y Seq\[V2\], f func\(V1, V2\) bool\) bool](<#EqualFunc>) @@ -48,6 +49,7 @@ WARNING: golang 1.23 has higher performance on iterating Seq/Seq2 which boost by - [func Reduce2\[Sum, K, V any\]\(f func\(Sum, K, V\) Sum, sum Sum, seq Seq2\[K, V\]\) Sum](<#Reduce2>) - [func Sum\[T constraints.Number\]\(seq Seq\[T\]\) T](<#Sum>) - [func ToMap\[K comparable, V any\]\(seq Seq2\[K, V\]\) \(out map\[K\]V\)](<#ToMap>) +- [func ToMapFromSeq\[K comparable, V any\]\(seq Seq\[K\], fn func\(k K\) V\) \(out map\[K\]V\)](<#ToMapFromSeq>) - [func ToSlice\[T any\]\(seq Seq\[T\]\) \(out \[\]T\)](<#ToSlice>) - [func ToSliceN\[T any\]\(seq Seq\[T\], n int\) \(out \[\]T\)](<#ToSliceN>) - [func ToSliceSeq2Key\[K, V any\]\(seq Seq2\[K, V\]\) \(out \[\]K\)](<#ToSliceSeq2Key>) @@ -94,7 +96,7 @@ WARNING: golang 1.23 has higher performance on iterating Seq/Seq2 which boost by -## func [AllFromSeq]() +## func [AllFromSeq]() ```go func AllFromSeq[T any](seq Seq[T], f func(T) bool) bool @@ -103,7 +105,7 @@ func AllFromSeq[T any](seq Seq[T], f func(T) bool) bool AllFromSeq return true if all elements from seq satisfy the condition evaluated by f. -## func [AnyFromSeq]() +## func [AnyFromSeq]() ```go func AnyFromSeq[T any](seq Seq[T], f func(T) bool) bool @@ -121,7 +123,7 @@ func At[T any](seq Seq[T], index int) optional.O[T] At return the element at index from seq. -## func [AvgByFromSeq]() +## func [AvgByFromSeq]() ```go func AvgByFromSeq[V any, T constraints.Number](seq Seq[V], f func(V) T) float64 @@ -130,7 +132,7 @@ func AvgByFromSeq[V any, T constraints.Number](seq Seq[V], f func(V) T) float64 AvgByFromSeq return the average value of all elements from seq, evaluated by f. -## func [AvgFromSeq]() +## func [AvgFromSeq]() ```go func AvgFromSeq[T constraints.Number](seq Seq[T]) float64 @@ -139,7 +141,7 @@ func AvgFromSeq[T constraints.Number](seq Seq[T]) float64 AvgFromSeq return the average value of all elements from seq. -## func [Contains]() +## func [Contains]() ```go func Contains[T comparable](seq Seq[T], in T) bool @@ -148,7 +150,7 @@ func Contains[T comparable](seq Seq[T], in T) bool Contains return true if v is in seq. -## func [ContainsAll]() +## func [ContainsAll]() ```go func ContainsAll[T comparable](seq Seq[T], in []T) bool @@ -157,7 +159,7 @@ func ContainsAll[T comparable](seq Seq[T], in []T) bool ContainsAll return true if all elements from seq is in vs. -## func [ContainsAny]() +## func [ContainsAny]() ```go func ContainsAny[T comparable](seq Seq[T], in []T) bool @@ -166,7 +168,7 @@ func ContainsAny[T comparable](seq Seq[T], in []T) bool ContainsAny return true if any element from seq is in vs. -## func [ContainsBy]() +## func [ContainsBy]() ```go func ContainsBy[T any](seq Seq[T], f func(T) bool) bool @@ -175,7 +177,7 @@ func ContainsBy[T any](seq Seq[T], f func(T) bool) bool ContainsBy return true if any element from seq satisfies the condition evaluated by f. -## func [Count]() +## func [Count]() ```go func Count[T any](seq Seq[T]) int @@ -183,6 +185,25 @@ func Count[T any](seq Seq[T]) int Count return the number of elements in seq. + +## func [Difference]() + +```go +func Difference[T comparable](left Seq[T], right Seq[T]) (onlyLeft Seq[T], onlyRight Seq[T]) +``` + +Difference returns two sequences: the first sequence contains elements that are in the left sequence but not in the right sequence, and the second sequence contains elements that are in the right sequence but not in the left sequence. + +EXAMPLE: + +``` +left := []int{1, 2, 3, 4} +right := []int{3, 4, 5, 6} +onlyLeft, onlyRight := Difference(FromSlice(left), FromSlice(right)) +// onlyLeft 👉 [1 2] +// onlyRight 👉 [5 6] +``` + ## func [Equal]() @@ -317,7 +338,7 @@ func EqualFunc2[K1, V1, K2, V2 any](x Seq2[K1, V1], y Seq2[K2, V2], f func(K1, V EqualFunc2 returns whether the two sequences are equal according to the function f. Like EqualFunc but run with Seq2 -## func [Find]() +## func [Find]() ```go func Find[T any](seq Seq[T], f func(T) bool) (val T, found bool) @@ -326,7 +347,7 @@ func Find[T any](seq Seq[T], f func(T) bool) (val T, found bool) Find return the first element from seq that satisfies the condition evaluated by f with a boolean representing whether it exists. -## func [FindO]() +## func [FindO]() ```go func FindO[T any](seq Seq[T], f func(T) bool) optional.O[T] @@ -335,7 +356,7 @@ func FindO[T any](seq Seq[T], f func(T) bool) optional.O[T] FindO return the first element from seq that satisfies the condition evaluated by f. -## func [First]() +## func [First]() ```go func First[T any](in Seq[T]) (T, bool) @@ -350,7 +371,7 @@ first, ok := First(seq) ``` -## func [FirstO]() +## func [FirstO]() ```go func FirstO[T any](in Seq[T]) optional.O[T] @@ -365,7 +386,7 @@ first, ok := FirstO(seq) ``` -## func [ForEach]() +## func [ForEach]() ```go func ForEach[T any](seq Seq[T], f func(T) bool) @@ -374,7 +395,7 @@ func ForEach[T any](seq Seq[T], f func(T) bool) ForEach execute f for each element in seq. -## func [ForEachIdx]() +## func [ForEachIdx]() ```go func ForEachIdx[T any](seq Seq[T], f func(idx int, v T) bool) @@ -383,7 +404,7 @@ func ForEachIdx[T any](seq Seq[T], f func(idx int, v T) bool) ForEachIdx execute f for each element in seq with its index. -## func [Head]() +## func [Head]() ```go func Head[T any](seq Seq[T]) (v T, hasOne bool) @@ -392,7 +413,7 @@ func Head[T any](seq Seq[T]) (v T, hasOne bool) Head return the first element from seq with a boolean representing whether it is at least one element in seq. -## func [HeadO]() +## func [HeadO]() ```go func HeadO[T any](seq Seq[T]) optional.O[T] @@ -401,7 +422,7 @@ func HeadO[T any](seq Seq[T]) optional.O[T] HeadO return the first element from seq. -## func [Index]() +## func [Index]() ```go func Index[T comparable](seq Seq[T], v T) int @@ -418,7 +439,7 @@ idx := xiter.Index(seq, 3) ``` -## func [Join]() +## func [Join]() ```go func Join[T ~string](seq Seq[T], sep T) T @@ -427,7 +448,7 @@ func Join[T ~string](seq Seq[T], sep T) T Join return the concatenation of all elements in seq with sep. -## func [Last]() +## func [Last]() ```go func Last[T any](in Seq[T]) (T, bool) @@ -442,7 +463,7 @@ last, ok := Last(seq) ``` -## func [LastO]() +## func [LastO]() ```go func LastO[T any](in Seq[T]) optional.O[T] @@ -457,7 +478,7 @@ last, ok := LastO(seq) ``` -## func [Max]() +## func [Max]() ```go func Max[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) @@ -466,7 +487,7 @@ func Max[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) Max returns the maximum element in seq. -## func [MaxBy]() +## func [MaxBy]() ```go func MaxBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional.O[T]) @@ -475,7 +496,7 @@ func MaxBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional. MaxBy return the maximum element in seq, evaluated by f. -## func [Min]() +## func [Min]() ```go func Min[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) @@ -484,7 +505,7 @@ func Min[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) Min return the minimum element in seq. -## func [MinBy]() +## func [MinBy]() ```go func MinBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional.O[T]) @@ -493,7 +514,7 @@ func MinBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional. MinBy return the minimum element in seq, evaluated by f. -## func [Pull]() +## func [Pull]() ```go func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()) @@ -544,7 +565,7 @@ func main() { -## func [Pull2]() +## func [Pull2]() ```go func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func()) @@ -621,7 +642,7 @@ func Reduce2[Sum, K, V any](f func(Sum, K, V) Sum, sum Sum, seq Seq2[K, V]) Sum Reduce2 combines the values in seq using f. For each pair k, v in seq, it updates sum = f\(sum, k, v\) and then returns the final sum. For example, if iterating over seq yields \(k1, v1\), \(k2, v2\), \(k3, v3\) Reduce returns f\(f\(f\(sum, k1, v1\), k2, v2\), k3, v3\). -## func [Sum]() +## func [Sum]() ```go func Sum[T constraints.Number](seq Seq[T]) T @@ -646,6 +667,15 @@ func ToMap[K comparable, V any](seq Seq2[K, V]) (out map[K]V) + +## func [ToMapFromSeq]() + +```go +func ToMapFromSeq[K comparable, V any](seq Seq[K], fn func(k K) V) (out map[K]V) +``` + + + ## func [ToSlice]() @@ -656,7 +686,7 @@ func ToSlice[T any](seq Seq[T]) (out []T) ToSlice returns the elements in seq as a slice. -## func [ToSliceN]() +## func [ToSliceN]() ```go func ToSliceN[T any](seq Seq[T], n int) (out []T) @@ -708,7 +738,7 @@ type Seq[V any] iter.Seq[V] ``` -### func [Chunk]() +### func [Chunk]() ```go func Chunk[T any](seq Seq[T], n int) Seq[[]T] @@ -725,7 +755,7 @@ chunkedSeq := xiter.Chunk(seq, 2) ``` -### func [Compact]() +### func [Compact]() ```go func Compact[T comparable](in Seq[T]) Seq[T] @@ -863,7 +893,7 @@ _ = ToSlice(seq) // Returns []int{1, 2} ``` -### func [FromMapKeys]() +### func [FromMapKeys]() ```go func FromMapKeys[K comparable, V any](m map[K]V) Seq[K] @@ -872,7 +902,7 @@ func FromMapKeys[K comparable, V any](m map[K]V) Seq[K] -### func [FromMapValues]() +### func [FromMapValues]() ```go func FromMapValues[K comparable, V any](m map[K]V) Seq[V] @@ -899,7 +929,7 @@ func FromSliceReverse[T any, Slice ~[]T](in Slice) Seq[T] -### func [FromSliceShuffle]() +### func [FromSliceShuffle]() ```go func FromSliceShuffle[T any](in []T) Seq[T] @@ -1074,7 +1104,7 @@ func Repeat[T any](seq Seq[T], count int) Seq[T] Repeat return a seq that repeat seq for count times. -### func [Replace]() +### func [Replace]() ```go func Replace[T comparable](seq Seq[T], from, to T, n int) Seq[T] @@ -1091,7 +1121,7 @@ replacedSeq := Replace(seq, 2, 99, -1) // Replace all 2s with 99 ``` -### func [ReplaceAll]() +### func [ReplaceAll]() ```go func ReplaceAll[T comparable](seq Seq[T], from, to T) Seq[T] @@ -1126,7 +1156,7 @@ func Seq2KeyToSeq[K, V any](in Seq2[K, V]) Seq[K] -### func [Seq2ToSeqUnion]() +### func [Seq2ToSeqUnion]() ```go func Seq2ToSeqUnion[K, V any](seq Seq2[K, V]) Seq[union.U2[K, V]] @@ -1153,7 +1183,7 @@ func Seq2ValueToSeq[K, V any](in Seq2[K, V]) Seq[V] -### func [Skip]() +### func [Skip]() ```go func Skip[T any](seq Seq[T], n int) Seq[T] @@ -1162,7 +1192,7 @@ func Skip[T any](seq Seq[T], n int) Seq[T] Skip return a seq that skip n elements from seq. -### func [Uniq]() +### func [Uniq]() ```go func Uniq[T comparable](seq Seq[T]) Seq[T] @@ -1286,7 +1316,7 @@ func Filter2[K, V any](f func(K, V) bool, seq Seq2[K, V]) Seq2[K, V] Filter2 returns an Seq over seq that only includes the key\-value pairs k, v for which f\(k, v\) is true. Like Filter but run with Seq2 -### func [FromMapKeyAndValues]() +### func [FromMapKeyAndValues]() ```go func FromMapKeyAndValues[K comparable, V any](m map[K]V) Seq2[K, V] @@ -1322,7 +1352,7 @@ func Map2[KIn, VIn, KOut, VOut any](f func(KIn, VIn) (KOut, VOut), seq Seq2[KIn, Map2 returns a Seq2 over the results of applying f to each key\-value pair in seq. Like Map but run with Seq2 -### func [MapToSeq2]() +### func [MapToSeq2]() ```go func MapToSeq2[T any, K comparable](in Seq[T], mapFn func(ele T) K) Seq2[K, T] @@ -1343,7 +1373,7 @@ fmt.Println(ToMap(lenMap)) ``` -### func [MapToSeq2Value]() +### func [MapToSeq2Value]() ```go func MapToSeq2Value[T any, K comparable, V any](in Seq[T], mapFn func(ele T) (K, V)) Seq2[K, V] diff --git a/pkg/xiter/xiter.go b/pkg/xiter/xiter.go index 9fb6c38..5296182 100644 --- a/pkg/xiter/xiter.go +++ b/pkg/xiter/xiter.go @@ -504,6 +504,14 @@ func ToMap[K comparable, V any](seq Seq2[K, V]) (out map[K]V) { return maps.Collect(iter.Seq2[K, V](seq)) } +func ToMapFromSeq[K comparable, V any](seq Seq[K], fn func(k K) V) (out map[K]V) { + out = make(map[K]V) + for k := range seq { + out[k] = fn(k) + } + return out +} + func FromMapKeys[K comparable, V any](m map[K]V) Seq[K] { return Seq[K](maps.Keys(m)) } diff --git a/pkg/xiter/xiter_common.go b/pkg/xiter/xiter_common.go index 7a3b2b3..56dab29 100644 --- a/pkg/xiter/xiter_common.go +++ b/pkg/xiter/xiter_common.go @@ -111,3 +111,31 @@ func FromChan[T any](in <-chan T) Seq[T] { } } } + +// Difference returns two sequences: the first sequence contains elements that are in the left sequence but not in the right sequence, +// and the second sequence contains elements that are in the right sequence but not in the left sequence. +// +// EXAMPLE: +// +// left := []int{1, 2, 3, 4} +// right := []int{3, 4, 5, 6} +// onlyLeft, onlyRight := Difference(FromSlice(left), FromSlice(right)) +// // onlyLeft 👉 [1 2] +// // onlyRight 👉 [5 6] +func Difference[T comparable](left Seq[T], right Seq[T]) (onlyLeft Seq[T], onlyRight Seq[T]) { + leftMap := ToMapFromSeq(left, func(k T) struct{} { + return struct{}{} + }) + rightMap := ToMapFromSeq(right, func(k T) struct{} { + return struct{}{} + }) + + return Filter(func(v T) bool { + _, ok := rightMap[v] + return !ok + }, left), + Filter(func(v T) bool { + _, ok := leftMap[v] + return !ok + }, right) +} diff --git a/pkg/xiter/xiter_common_test.go b/pkg/xiter/xiter_common_test.go index 24eab63..c7a11e6 100644 --- a/pkg/xiter/xiter_common_test.go +++ b/pkg/xiter/xiter_common_test.go @@ -1,9 +1,10 @@ package xiter_test import ( + "testing" + "github.com/dashjay/xiter/pkg/xiter" "github.com/stretchr/testify/assert" - "testing" ) func TestXIterCommon(t *testing.T) { @@ -72,4 +73,12 @@ func TestXIterCommon(t *testing.T) { seq := xiter.FromChan(ch) testLimit(t, seq, 1) }) + + t.Run("difference", func(t *testing.T) { + left := xiter.FromSlice(_range(0, 10)) + right := xiter.FromSlice(_range(5, 15)) + onlyLeft, onlyRight := xiter.Difference(left, right) + assert.Equal(t, _range(0, 5), xiter.ToSlice(onlyLeft)) + assert.Equal(t, _range(10, 15), xiter.ToSlice(onlyRight)) + }) } diff --git a/pkg/xiter/xiter_old.go b/pkg/xiter/xiter_old.go index 0460c60..1f5a114 100644 --- a/pkg/xiter/xiter_old.go +++ b/pkg/xiter/xiter_old.go @@ -349,6 +349,15 @@ func ToMap[K comparable, V any](seq Seq2[K, V]) (out map[K]V) { return out } +func ToMapFromSeq[K comparable, V any](seq Seq[K], fn func(k K) V) (out map[K]V) { + out = make(map[K]V) + seq(func(k K) bool { + out[k] = fn(k) + return true + }) + return out +} + func FromMapKeys[K comparable, V any](m map[K]V) Seq[K] { return func(yield func(K) bool) { for k := range m { diff --git a/pkg/xslice/README.md b/pkg/xslice/README.md index 35c5417..f89a402 100644 --- a/pkg/xslice/README.md +++ b/pkg/xslice/README.md @@ -24,6 +24,7 @@ import "github.com/dashjay/xiter/pkg/xslice" - [func ContainsAny\[T comparable\]\(in \[\]T, v \[\]T\) bool](<#ContainsAny>) - [func ContainsBy\[T any\]\(in \[\]T, f func\(T\) bool\) bool](<#ContainsBy>) - [func Count\[T any\]\(in \[\]T\) int](<#Count>) +- [func Difference\[T comparable, Slice \~\[\]T\]\(left, right Slice\) \(onlyLeft, onlyRight Slice\)](<#Difference>) - [func Filter\[T any, Slice \~\[\]T\]\(in Slice, f func\(T\) bool\) Slice](<#Filter>) - [func Find\[T any\]\(in \[\]T, f func\(T\) bool\) \(val T, found bool\)](<#Find>) - [func FindO\[T any\]\(in \[\]T, f func\(T\) bool\) optional.O\[T\]](<#FindO>) @@ -327,6 +328,25 @@ xslice.Count([]int{1, 2, 3}) 👉 3 xslice.Count([]int{}) 👉 0 ``` + +## func [Difference]() + +```go +func Difference[T comparable, Slice ~[]T](left, right Slice) (onlyLeft, onlyRight Slice) +``` + +Difference returns two slices: the first slice contains the elements that are in the left slice but not in the right slice, and the second slice contains the elements that are in the right slice but not in the left slice. + +EXAMPLE: + +``` +left := []int{1, 2, 3, 4, 5} +right := []int{4, 5, 6, 7, 8} +onlyLeft, onlyRight := xslice.Difference(left, right) +fmt.Println(onlyLeft) // [1 2 3] +fmt.Println(onlyRight) // [6 7 8] +``` + ## func [Filter]() diff --git a/pkg/xslice/xslice.go b/pkg/xslice/xslice.go index 4029daf..755cfa9 100644 --- a/pkg/xslice/xslice.go +++ b/pkg/xslice/xslice.go @@ -625,3 +625,18 @@ func Last[T any, Slice ~[]T](in Slice) (T, bool) { func LastO[T any, Slice ~[]T](in Slice) optional.O[T] { return optional.FromValue2(Last(in)) } + +// Difference returns two slices: the first slice contains the elements that are in the left slice but not in the right slice, +// and the second slice contains the elements that are in the right slice but not in the left slice. +// +// EXAMPLE: +// +// left := []int{1, 2, 3, 4, 5} +// right := []int{4, 5, 6, 7, 8} +// onlyLeft, onlyRight := xslice.Difference(left, right) +// fmt.Println(onlyLeft) // [1 2 3] +// fmt.Println(onlyRight) // [6 7 8] +func Difference[T comparable, Slice ~[]T](left, right Slice) (onlyLeft, onlyRight Slice) { + onlyLeftSeq, onlyRightSeq := xiter.Difference(xiter.FromSlice(left), xiter.FromSlice(right)) + return xiter.ToSlice(onlyLeftSeq), xiter.ToSlice(onlyRightSeq) +} diff --git a/pkg/xslice/xslice_benchmark_test.go b/pkg/xslice/xslice_benchmark_test.go index 56da493..ce85966 100644 --- a/pkg/xslice/xslice_benchmark_test.go +++ b/pkg/xslice/xslice_benchmark_test.go @@ -183,6 +183,21 @@ func BenchmarkSlice(b *testing.B) { } }) }) + + b.Run("benchmark difference", func(b *testing.B) { + left := _range(0, 1000) + right := _range(500, 1500) + b.Run("xslice", func(b *testing.B) { + for i := 0; i < b.N; i++ { + xslice.Difference(left, right) + } + }) + b.Run("lo", func(b *testing.B) { + for i := 0; i < b.N; i++ { + lo.Difference(left, right) + } + }) + }) } func BenchmarkChunk(b *testing.B) { diff --git a/pkg/xslice/xslice_test.go b/pkg/xslice/xslice_test.go index 84e66e1..737c281 100644 --- a/pkg/xslice/xslice_test.go +++ b/pkg/xslice/xslice_test.go @@ -331,9 +331,20 @@ func TestSlices(t *testing.T) { t.Run("first", func(t *testing.T) { assert.Equal(t, 1, xslice.FirstO([]int{1, 2, 3}).Must()) + assert.False(t, xslice.FirstO([]int{}).Ok()) }) t.Run("last", func(t *testing.T) { assert.Equal(t, 3, xslice.LastO([]int{1, 2, 3}).Must()) + assert.False(t, xslice.LastO([]int{}).Ok()) }) + + t.Run("difference", func(t *testing.T) { + left := []int{1, 2, 3, 4, 5} + right := []int{4, 5, 6, 7, 8} + onlyLeft, onlyRight := xslice.Difference(left, right) + assert.Equal(t, []int{1, 2, 3}, onlyLeft) + assert.Equal(t, []int{6, 7, 8}, onlyRight) + }) + } From c686b938d2bf706bfc7ec04e7598a277548915d8 Mon Sep 17 00:00:00 2001 From: dashjay Date: Thu, 24 Jul 2025 22:30:06 +0800 Subject: [PATCH 6/8] implement intersect --- pkg/xslice/README.md | 10 ++++++++++ pkg/xslice/xslice.go | 19 +++++++++++++++++++ pkg/xslice/xslice_test.go | 9 +++++++++ 3 files changed, 38 insertions(+) diff --git a/pkg/xslice/README.md b/pkg/xslice/README.md index f89a402..88b5671 100644 --- a/pkg/xslice/README.md +++ b/pkg/xslice/README.md @@ -37,6 +37,7 @@ import "github.com/dashjay/xiter/pkg/xslice" - [func Head\[T any\]\(in \[\]T\) \(v T, hasOne bool\)](<#Head>) - [func HeadO\[T any\]\(in \[\]T\) optional.O\[T\]](<#HeadO>) - [func Index\[T comparable, Slice \~\[\]T\]\(in Slice, v T\) int](<#Index>) +- [func Intersect\[T comparable, Slice \~\[\]T\]\(left, right Slice\) Slice](<#Intersect>) - [func Join\[T \~string\]\(in \[\]T, sep T\) T](<#Join>) - [func Last\[T any, Slice \~\[\]T\]\(in Slice\) \(T, bool\)](<#Last>) - [func LastO\[T any, Slice \~\[\]T\]\(in Slice\) optional.O\[T\]](<#LastO>) @@ -543,6 +544,15 @@ xslice.Index([]int{1, 2, 3, 4, 5}, 3) 👉 2 xslice.Index([]int{1, 2, 3, 4, 5}, 666) 👉 -1 ``` + +## func [Intersect]() + +```go +func Intersect[T comparable, Slice ~[]T](left, right Slice) Slice +``` + + + ## func [Join]() diff --git a/pkg/xslice/xslice.go b/pkg/xslice/xslice.go index 755cfa9..44f06c9 100644 --- a/pkg/xslice/xslice.go +++ b/pkg/xslice/xslice.go @@ -640,3 +640,22 @@ func Difference[T comparable, Slice ~[]T](left, right Slice) (onlyLeft, onlyRigh onlyLeftSeq, onlyRightSeq := xiter.Difference(xiter.FromSlice(left), xiter.FromSlice(right)) return xiter.ToSlice(onlyLeftSeq), xiter.ToSlice(onlyRightSeq) } + +func Intersect[T comparable, Slice ~[]T](left, right Slice) Slice { + var smaller, larger Slice + if len(left) > len(right) { + smaller, larger = right, left + } else { + smaller, larger = left, right + } + smallerMap := xiter.ToMapFromSeq(xiter.FromSlice(smaller), func(T) struct{} { + return struct{}{} + }) + + return xiter.ToSlice( + xiter.Filter(func(v T) bool { + _, exists := smallerMap[v] + return exists + }, xiter.FromSlice(larger)), + ) +} diff --git a/pkg/xslice/xslice_test.go b/pkg/xslice/xslice_test.go index 737c281..ee0e0b0 100644 --- a/pkg/xslice/xslice_test.go +++ b/pkg/xslice/xslice_test.go @@ -347,4 +347,13 @@ func TestSlices(t *testing.T) { assert.Equal(t, []int{6, 7, 8}, onlyRight) }) + t.Run("intersect", func(t *testing.T) { + left := []int{1, 2, 3, 4, 5, 6} + right := []int{4, 5, 6, 7, 8} + assert.Equal(t, []int{4, 5, 6}, xslice.Intersect(left, right)) + assert.Equal(t, []int{4, 5, 6}, xslice.Intersect(right, left)) + + assert.Equal(t, left, xslice.Intersect(left, left)) + }) + } From 6f4823529143e682a40130ec5137fb667a368f16 Mon Sep 17 00:00:00 2001 From: dashjay Date: Sat, 26 Jul 2025 20:10:30 +0800 Subject: [PATCH 7/8] implement more --- pkg/xiter/README.md | 224 ++++++++++++++++++++++++--------- pkg/xiter/xiter.go | 2 + pkg/xiter/xiter_common.go | 99 +++++++++++++++ pkg/xiter/xiter_common_test.go | 47 +++++++ pkg/xiter/xiter_test.go | 1 + pkg/xslice/README.md | 30 ++++- pkg/xslice/xslice.go | 35 ++++-- pkg/xslice/xslice_test.go | 8 ++ 8 files changed, 378 insertions(+), 68 deletions(-) diff --git a/pkg/xiter/README.md b/pkg/xiter/README.md index 3f280f4..3becc14 100644 --- a/pkg/xiter/README.md +++ b/pkg/xiter/README.md @@ -41,8 +41,12 @@ WARNING: golang 1.23 has higher performance on iterating Seq/Seq2 which boost by - [func LastO\[T any\]\(in Seq\[T\]\) optional.O\[T\]](<#LastO>) - [func Max\[T constraints.Ordered\]\(seq Seq\[T\]\) \(r optional.O\[T\]\)](<#Max>) - [func MaxBy\[T constraints.Ordered\]\(seq Seq\[T\], less func\(T, T\) bool\) \(r optional.O\[T\]\)](<#MaxBy>) +- [func Mean\[T constraints.Number\]\(in Seq\[T\]\) T](<#Mean>) +- [func MeanBy\[T any, R constraints.Number\]\(in Seq\[T\], fn func\(T\) R\) R](<#MeanBy>) - [func Min\[T constraints.Ordered\]\(seq Seq\[T\]\) \(r optional.O\[T\]\)](<#Min>) - [func MinBy\[T constraints.Ordered\]\(seq Seq\[T\], less func\(T, T\) bool\) \(r optional.O\[T\]\)](<#MinBy>) +- [func Moderate\[T comparable\]\(in Seq\[T\]\) \(T, bool\)](<#Moderate>) +- [func ModerateO\[T constraints.Number\]\(in Seq\[T\]\) optional.O\[T\]](<#ModerateO>) - [func Pull\[V any\]\(seq Seq\[V\]\) \(next func\(\) \(V, bool\), stop func\(\)\)](<#Pull>) - [func Pull2\[K, V any\]\(seq Seq2\[K, V\]\) \(next func\(\) \(K, V, bool\), stop func\(\)\)](<#Pull2>) - [func Reduce\[Sum, V any\]\(f func\(Sum, V\) Sum, sum Sum, seq Seq\[V\]\) Sum](<#Reduce>) @@ -65,6 +69,7 @@ WARNING: golang 1.23 has higher performance on iterating Seq/Seq2 which boost by - [func FromSlice\[T any\]\(in \[\]T\) Seq\[T\]](<#FromSlice>) - [func FromSliceReverse\[T any, Slice \~\[\]T\]\(in Slice\) Seq\[T\]](<#FromSliceReverse>) - [func FromSliceShuffle\[T any\]\(in \[\]T\) Seq\[T\]](<#FromSliceShuffle>) + - [func Intersect\[T comparable\]\(left Seq\[T\], right Seq\[T\]\) Seq\[T\]](<#Intersect>) - [func Limit\[V any\]\(seq Seq\[V\], n int\) Seq\[V\]](<#Limit>) - [func Map\[In, Out any\]\(f func\(In\) Out, seq Seq\[In\]\) Seq\[Out\]](<#Map>) - [func Merge\[V cmp.Ordered\]\(x, y Seq\[V\]\) Seq\[V\]](<#Merge>) @@ -77,6 +82,7 @@ WARNING: golang 1.23 has higher performance on iterating Seq/Seq2 which boost by - [func Seq2ToSeqUnion\[K, V any\]\(seq Seq2\[K, V\]\) Seq\[union.U2\[K, V\]\]](<#Seq2ToSeqUnion>) - [func Seq2ValueToSeq\[K, V any\]\(in Seq2\[K, V\]\) Seq\[V\]](<#Seq2ValueToSeq>) - [func Skip\[T any\]\(seq Seq\[T\], n int\) Seq\[T\]](<#Skip>) + - [func Union\[T comparable\]\(left, right Seq\[T\]\) Seq\[T\]](<#Union>) - [func Uniq\[T comparable\]\(seq Seq\[T\]\) Seq\[T\]](<#Uniq>) - [func Zip\[V1, V2 any\]\(x Seq\[V1\], y Seq\[V2\]\) Seq\[Zipped\[V1, V2\]\]](<#Zip>) - [func Zip2\[K1, V1, K2, V2 any\]\(x Seq2\[K1, V1\], y Seq2\[K2, V2\]\) Seq\[Zipped2\[K1, V1, K2, V2\]\]](<#Zip2>) @@ -96,7 +102,7 @@ WARNING: golang 1.23 has higher performance on iterating Seq/Seq2 which boost by -## func [AllFromSeq]() +## func [AllFromSeq]() ```go func AllFromSeq[T any](seq Seq[T], f func(T) bool) bool @@ -105,7 +111,7 @@ func AllFromSeq[T any](seq Seq[T], f func(T) bool) bool AllFromSeq return true if all elements from seq satisfy the condition evaluated by f. -## func [AnyFromSeq]() +## func [AnyFromSeq]() ```go func AnyFromSeq[T any](seq Seq[T], f func(T) bool) bool @@ -114,7 +120,7 @@ func AnyFromSeq[T any](seq Seq[T], f func(T) bool) bool AnyFromSeq return true if any elements from seq satisfy the condition evaluated by f. -## func [At]() +## func [At]() ```go func At[T any](seq Seq[T], index int) optional.O[T] @@ -123,7 +129,7 @@ func At[T any](seq Seq[T], index int) optional.O[T] At return the element at index from seq. -## func [AvgByFromSeq]() +## func [AvgByFromSeq]() ```go func AvgByFromSeq[V any, T constraints.Number](seq Seq[V], f func(V) T) float64 @@ -132,7 +138,7 @@ func AvgByFromSeq[V any, T constraints.Number](seq Seq[V], f func(V) T) float64 AvgByFromSeq return the average value of all elements from seq, evaluated by f. -## func [AvgFromSeq]() +## func [AvgFromSeq]() ```go func AvgFromSeq[T constraints.Number](seq Seq[T]) float64 @@ -141,7 +147,7 @@ func AvgFromSeq[T constraints.Number](seq Seq[T]) float64 AvgFromSeq return the average value of all elements from seq. -## func [Contains]() +## func [Contains]() ```go func Contains[T comparable](seq Seq[T], in T) bool @@ -150,7 +156,7 @@ func Contains[T comparable](seq Seq[T], in T) bool Contains return true if v is in seq. -## func [ContainsAll]() +## func [ContainsAll]() ```go func ContainsAll[T comparable](seq Seq[T], in []T) bool @@ -159,7 +165,7 @@ func ContainsAll[T comparable](seq Seq[T], in []T) bool ContainsAll return true if all elements from seq is in vs. -## func [ContainsAny]() +## func [ContainsAny]() ```go func ContainsAny[T comparable](seq Seq[T], in []T) bool @@ -168,7 +174,7 @@ func ContainsAny[T comparable](seq Seq[T], in []T) bool ContainsAny return true if any element from seq is in vs. -## func [ContainsBy]() +## func [ContainsBy]() ```go func ContainsBy[T any](seq Seq[T], f func(T) bool) bool @@ -177,7 +183,7 @@ func ContainsBy[T any](seq Seq[T], f func(T) bool) bool ContainsBy return true if any element from seq satisfies the condition evaluated by f. -## func [Count]() +## func [Count]() ```go func Count[T any](seq Seq[T]) int @@ -186,7 +192,7 @@ func Count[T any](seq Seq[T]) int Count return the number of elements in seq. -## func [Difference]() +## func [Difference]() ```go func Difference[T comparable](left Seq[T], right Seq[T]) (onlyLeft Seq[T], onlyRight Seq[T]) @@ -338,7 +344,7 @@ func EqualFunc2[K1, V1, K2, V2 any](x Seq2[K1, V1], y Seq2[K2, V2], f func(K1, V EqualFunc2 returns whether the two sequences are equal according to the function f. Like EqualFunc but run with Seq2 -## func [Find]() +## func [Find]() ```go func Find[T any](seq Seq[T], f func(T) bool) (val T, found bool) @@ -347,7 +353,7 @@ func Find[T any](seq Seq[T], f func(T) bool) (val T, found bool) Find return the first element from seq that satisfies the condition evaluated by f with a boolean representing whether it exists. -## func [FindO]() +## func [FindO]() ```go func FindO[T any](seq Seq[T], f func(T) bool) optional.O[T] @@ -356,7 +362,7 @@ func FindO[T any](seq Seq[T], f func(T) bool) optional.O[T] FindO return the first element from seq that satisfies the condition evaluated by f. -## func [First]() +## func [First]() ```go func First[T any](in Seq[T]) (T, bool) @@ -371,7 +377,7 @@ first, ok := First(seq) ``` -## func [FirstO]() +## func [FirstO]() ```go func FirstO[T any](in Seq[T]) optional.O[T] @@ -386,7 +392,7 @@ first, ok := FirstO(seq) ``` -## func [ForEach]() +## func [ForEach]() ```go func ForEach[T any](seq Seq[T], f func(T) bool) @@ -395,7 +401,7 @@ func ForEach[T any](seq Seq[T], f func(T) bool) ForEach execute f for each element in seq. -## func [ForEachIdx]() +## func [ForEachIdx]() ```go func ForEachIdx[T any](seq Seq[T], f func(idx int, v T) bool) @@ -404,7 +410,7 @@ func ForEachIdx[T any](seq Seq[T], f func(idx int, v T) bool) ForEachIdx execute f for each element in seq with its index. -## func [Head]() +## func [Head]() ```go func Head[T any](seq Seq[T]) (v T, hasOne bool) @@ -413,7 +419,7 @@ func Head[T any](seq Seq[T]) (v T, hasOne bool) Head return the first element from seq with a boolean representing whether it is at least one element in seq. -## func [HeadO]() +## func [HeadO]() ```go func HeadO[T any](seq Seq[T]) optional.O[T] @@ -422,7 +428,7 @@ func HeadO[T any](seq Seq[T]) optional.O[T] HeadO return the first element from seq. -## func [Index]() +## func [Index]() ```go func Index[T comparable](seq Seq[T], v T) int @@ -439,7 +445,7 @@ idx := xiter.Index(seq, 3) ``` -## func [Join]() +## func [Join]() ```go func Join[T ~string](seq Seq[T], sep T) T @@ -448,7 +454,7 @@ func Join[T ~string](seq Seq[T], sep T) T Join return the concatenation of all elements in seq with sep. -## func [Last]() +## func [Last]() ```go func Last[T any](in Seq[T]) (T, bool) @@ -463,7 +469,7 @@ last, ok := Last(seq) ``` -## func [LastO]() +## func [LastO]() ```go func LastO[T any](in Seq[T]) optional.O[T] @@ -478,7 +484,7 @@ last, ok := LastO(seq) ``` -## func [Max]() +## func [Max]() ```go func Max[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) @@ -487,7 +493,7 @@ func Max[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) Max returns the maximum element in seq. -## func [MaxBy]() +## func [MaxBy]() ```go func MaxBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional.O[T]) @@ -495,8 +501,42 @@ func MaxBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional. MaxBy return the maximum element in seq, evaluated by f. + +## func [Mean]() + +```go +func Mean[T constraints.Number](in Seq[T]) T +``` + +Mean return the mean of seq. + +EXAMPLE: + +``` +mean := Mean(FromSlice([]int{1, 2, 3, 4, 5})) +// mean 👉 3 +``` + + +## func [MeanBy]() + +```go +func MeanBy[T any, R constraints.Number](in Seq[T], fn func(T) R) R +``` + +MeanBy return the mean of seq by fn. + +EXAMPLE: + +``` +mean := MeanBy(FromSlice([]int{1, 2, 3, 4, 5}), func(v int) int { + return v * 2 +}) +// mean 👉 6 +``` + -## func [Min]() +## func [Min]() ```go func Min[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) @@ -505,7 +545,7 @@ func Min[T constraints.Ordered](seq Seq[T]) (r optional.O[T]) Min return the minimum element in seq. -## func [MinBy]() +## func [MinBy]() ```go func MinBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional.O[T]) @@ -513,8 +553,40 @@ func MinBy[T constraints.Ordered](seq Seq[T], less func(T, T) bool) (r optional. MinBy return the minimum element in seq, evaluated by f. + +## func [Moderate]() + +```go +func Moderate[T comparable](in Seq[T]) (T, bool) +``` + +Moderate return the most common element in seq. + +EXAMPLE: + +``` +moderate := Moderate(FromSlice([]int{1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 6})) +// moderate 👉 6 +``` + + +## func [ModerateO]() + +```go +func ModerateO[T constraints.Number](in Seq[T]) optional.O[T] +``` + +ModerateO return the most common element in seq. + +EXAMPLE: + +``` +moderate := ModerateO(FromSlice([]int{1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 6})) +// moderate 👉 6 +``` + -## func [Pull]() +## func [Pull]() ```go func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()) @@ -565,7 +637,7 @@ func main() { -## func [Pull2]() +## func [Pull2]() ```go func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func()) @@ -642,7 +714,7 @@ func Reduce2[Sum, K, V any](f func(Sum, K, V) Sum, sum Sum, seq Seq2[K, V]) Sum Reduce2 combines the values in seq using f. For each pair k, v in seq, it updates sum = f\(sum, k, v\) and then returns the final sum. For example, if iterating over seq yields \(k1, v1\), \(k2, v2\), \(k3, v3\) Reduce returns f\(f\(f\(sum, k1, v1\), k2, v2\), k3, v3\). -## func [Sum]() +## func [Sum]() ```go func Sum[T constraints.Number](seq Seq[T]) T @@ -659,7 +731,7 @@ sum := xiter.Sum(seq) ``` -## func [ToMap]() +## func [ToMap]() ```go func ToMap[K comparable, V any](seq Seq2[K, V]) (out map[K]V) @@ -668,7 +740,7 @@ func ToMap[K comparable, V any](seq Seq2[K, V]) (out map[K]V) -## func [ToMapFromSeq]() +## func [ToMapFromSeq]() ```go func ToMapFromSeq[K comparable, V any](seq Seq[K], fn func(k K) V) (out map[K]V) @@ -686,7 +758,7 @@ func ToSlice[T any](seq Seq[T]) (out []T) ToSlice returns the elements in seq as a slice. -## func [ToSliceN]() +## func [ToSliceN]() ```go func ToSliceN[T any](seq Seq[T], n int) (out []T) @@ -738,7 +810,7 @@ type Seq[V any] iter.Seq[V] ``` -### func [Chunk]() +### func [Chunk]() ```go func Chunk[T any](seq Seq[T], n int) Seq[[]T] @@ -755,7 +827,7 @@ chunkedSeq := xiter.Chunk(seq, 2) ``` -### func [Compact]() +### func [Compact]() ```go func Compact[T comparable](in Seq[T]) Seq[T] @@ -870,7 +942,7 @@ func main() { -### func [FromChan]() +### func [FromChan]() ```go func FromChan[T any](in <-chan T) Seq[T] @@ -893,7 +965,7 @@ _ = ToSlice(seq) // Returns []int{1, 2} ``` -### func [FromMapKeys]() +### func [FromMapKeys]() ```go func FromMapKeys[K comparable, V any](m map[K]V) Seq[K] @@ -902,7 +974,7 @@ func FromMapKeys[K comparable, V any](m map[K]V) Seq[K] -### func [FromMapValues]() +### func [FromMapValues]() ```go func FromMapValues[K comparable, V any](m map[K]V) Seq[V] @@ -911,7 +983,7 @@ func FromMapValues[K comparable, V any](m map[K]V) Seq[V] -### func [FromSlice]() +### func [FromSlice]() ```go func FromSlice[T any](in []T) Seq[T] @@ -920,7 +992,7 @@ func FromSlice[T any](in []T) Seq[T] FromSlice received a slice and returned a Seq for this slice. -### func [FromSliceReverse]() +### func [FromSliceReverse]() ```go func FromSliceReverse[T any, Slice ~[]T](in Slice) Seq[T] @@ -929,7 +1001,7 @@ func FromSliceReverse[T any, Slice ~[]T](in Slice) Seq[T] -### func [FromSliceShuffle]() +### func [FromSliceShuffle]() ```go func FromSliceShuffle[T any](in []T) Seq[T] @@ -945,6 +1017,24 @@ shuffledSeq := FromSliceShuffle(ToSlice(seq)) // shuffledSeq will yield a shuffled sequence of 1, 2, 3, 4, 5 ``` + +### func [Intersect]() + +```go +func Intersect[T comparable](left Seq[T], right Seq[T]) Seq[T] +``` + +Intersect return a seq that only contain elements in both left and right. + +EXAMPLE: + +``` +left := []int{1, 2, 3, 4} +right := []int{3, 4, 5, 6} +intersect := Intersect(FromSlice(left), FromSlice(right)) +// intersect 👉 [3 4] +``` + ### func [Limit]() @@ -1095,7 +1185,7 @@ func MergeFunc[V any](x, y Seq[V], f func(V, V) int) Seq[V] MergeFunc merges two sequences of values ordered by the function f. Values appear in the output once for each time they appear in x and once for each time they appear in y. When equal values appear in both sequences, the output contains the values from x before the values from y. If the two input sequences are not ordered by f, the output sequence will not be ordered by f, but it will still contain every value from x and y exactly once. -### func [Repeat]() +### func [Repeat]() ```go func Repeat[T any](seq Seq[T], count int) Seq[T] @@ -1104,7 +1194,7 @@ func Repeat[T any](seq Seq[T], count int) Seq[T] Repeat return a seq that repeat seq for count times. -### func [Replace]() +### func [Replace]() ```go func Replace[T comparable](seq Seq[T], from, to T, n int) Seq[T] @@ -1121,7 +1211,7 @@ replacedSeq := Replace(seq, 2, 99, -1) // Replace all 2s with 99 ``` -### func [ReplaceAll]() +### func [ReplaceAll]() ```go func ReplaceAll[T comparable](seq Seq[T], from, to T) Seq[T] @@ -1138,7 +1228,7 @@ replacedSeq := ReplaceAll(seq, 2, 99) ``` -### func [Reverse]() +### func [Reverse]() ```go func Reverse[T any](seq Seq[T]) Seq[T] @@ -1147,16 +1237,16 @@ func Reverse[T any](seq Seq[T]) Seq[T] Reverse return a reversed seq. -### func [Seq2KeyToSeq]() +### func [Seq2KeyToSeq]() ```go func Seq2KeyToSeq[K, V any](in Seq2[K, V]) Seq[K] ``` - +Seq2KeyToSeq return a seq that only contain keys in seq2. -### func [Seq2ToSeqUnion]() +### func [Seq2ToSeqUnion]() ```go func Seq2ToSeqUnion[K, V any](seq Seq2[K, V]) Seq[union.U2[K, V]] @@ -1174,16 +1264,16 @@ for v := range Seq2ToSeqUnion(seq2) { ``` -### func [Seq2ValueToSeq]() +### func [Seq2ValueToSeq]() ```go func Seq2ValueToSeq[K, V any](in Seq2[K, V]) Seq[V] ``` - +Seq2ValueToSeq return a seq that only contain values in seq2. -### func [Skip]() +### func [Skip]() ```go func Skip[T any](seq Seq[T], n int) Seq[T] @@ -1191,8 +1281,26 @@ func Skip[T any](seq Seq[T], n int) Seq[T] Skip return a seq that skip n elements from seq. + +### func [Union]() + +```go +func Union[T comparable](left, right Seq[T]) Seq[T] +``` + +Union return a seq that contain all elements in left and right. + +EXAMPLE: + +``` +left := []int{1, 2, 3, 4} +right := []int{3, 4, 5, 6} +union := Union(FromSlice(left), FromSlice(right)) +// union 👉 [1 2 3 4 5 6] +``` + -### func [Uniq]() +### func [Uniq]() ```go func Uniq[T comparable](seq Seq[T]) Seq[T] @@ -1316,7 +1424,7 @@ func Filter2[K, V any](f func(K, V) bool, seq Seq2[K, V]) Seq2[K, V] Filter2 returns an Seq over seq that only includes the key\-value pairs k, v for which f\(k, v\) is true. Like Filter but run with Seq2 -### func [FromMapKeyAndValues]() +### func [FromMapKeyAndValues]() ```go func FromMapKeyAndValues[K comparable, V any](m map[K]V) Seq2[K, V] @@ -1325,7 +1433,7 @@ func FromMapKeyAndValues[K comparable, V any](m map[K]V) Seq2[K, V] -### func [FromSliceIdx]() +### func [FromSliceIdx]() ```go func FromSliceIdx[T any](in []T) Seq2[int, T] @@ -1352,7 +1460,7 @@ func Map2[KIn, VIn, KOut, VOut any](f func(KIn, VIn) (KOut, VOut), seq Seq2[KIn, Map2 returns a Seq2 over the results of applying f to each key\-value pair in seq. Like Map but run with Seq2 -### func [MapToSeq2]() +### func [MapToSeq2]() ```go func MapToSeq2[T any, K comparable](in Seq[T], mapFn func(ele T) K) Seq2[K, T] @@ -1373,7 +1481,7 @@ fmt.Println(ToMap(lenMap)) ``` -### func [MapToSeq2Value]() +### func [MapToSeq2Value]() ```go func MapToSeq2Value[T any, K comparable, V any](in Seq[T], mapFn func(ele T) (K, V)) Seq2[K, V] @@ -1413,7 +1521,7 @@ func MergeFunc2[K, V any](x, y Seq2[K, V], f func(K, K) int) Seq2[K, V] MergeFunc2 merges two sequences of key\-value pairs ordered by the function f. Pairs appear in the output once for each time they appear in x and once for each time they appear in y. When pairs with equal keys appear in both sequences, the output contains the pairs from x before the pairs from y. If the two input sequences are not ordered by f, the output sequence will not be ordered by f, but it will still contain every pair from x and y exactly once. -## type [Zipped]() +## type [Zipped]() A Zipped is a pair of zipped values, one of which may be missing, drawn from two different sequences. @@ -1427,7 +1535,7 @@ type Zipped[V1, V2 any] struct { ``` -## type [Zipped2]() +## type [Zipped2]() A Zipped2 is a pair of zipped key\-value pairs, one of which may be missing, drawn from two different sequences. diff --git a/pkg/xiter/xiter.go b/pkg/xiter/xiter.go index 5296182..8ec7f73 100644 --- a/pkg/xiter/xiter.go +++ b/pkg/xiter/xiter.go @@ -480,6 +480,7 @@ func ToSliceSeq2Value[K, V any](seq Seq2[K, V]) (out []V) { return } +// Seq2KeyToSeq return a seq that only contain keys in seq2. func Seq2KeyToSeq[K, V any](in Seq2[K, V]) Seq[K] { return func(yield func(K) bool) { for k := range in { @@ -490,6 +491,7 @@ func Seq2KeyToSeq[K, V any](in Seq2[K, V]) Seq[K] { } } +// Seq2ValueToSeq return a seq that only contain values in seq2. func Seq2ValueToSeq[K, V any](in Seq2[K, V]) Seq[V] { return func(yield func(V) bool) { for _, v := range in { diff --git a/pkg/xiter/xiter_common.go b/pkg/xiter/xiter_common.go index 56dab29..2fd3c4f 100644 --- a/pkg/xiter/xiter_common.go +++ b/pkg/xiter/xiter_common.go @@ -1,6 +1,7 @@ package xiter import ( + "github.com/dashjay/xiter/pkg/internal/constraints" gassert "github.com/dashjay/xiter/pkg/internal/xassert" "github.com/dashjay/xiter/pkg/optional" ) @@ -139,3 +140,101 @@ func Difference[T comparable](left Seq[T], right Seq[T]) (onlyLeft Seq[T], onlyR return !ok }, right) } + +// Intersect return a seq that only contain elements in both left and right. +// +// EXAMPLE: +// +// left := []int{1, 2, 3, 4} +// right := []int{3, 4, 5, 6} +// intersect := Intersect(FromSlice(left), FromSlice(right)) +// // intersect 👉 [3 4] +func Intersect[T comparable](left Seq[T], right Seq[T]) Seq[T] { + leftMap := ToMapFromSeq(left, func(k T) struct{} { + return struct{}{} + }) + return Filter(func(v T) bool { + _, exists := leftMap[v] + return exists + }, right) +} + +// Union return a seq that contain all elements in left and right. +// +// EXAMPLE: +// +// left := []int{1, 2, 3, 4} +// right := []int{3, 4, 5, 6} +// union := Union(FromSlice(left), FromSlice(right)) +// // union 👉 [1 2 3 4 5 6] +func Union[T comparable](left, right Seq[T]) Seq[T] { + leftMap := ToMapFromSeq(left, func(k T) struct{} { + return struct{}{} + }) + return Concat(left, Filter(func(v T) bool { + _, exists := leftMap[v] + return !exists + }, right)) +} + +// Mean return the mean of seq. +// +// EXAMPLE: +// +// mean := Mean(FromSlice([]int{1, 2, 3, 4, 5})) +// // mean 👉 3 +func Mean[T constraints.Number](in Seq[T]) T { + var count T = 0 + s := Reduce(func(sum T, v T) T { + count++ + return sum + v + }, 0, in) + return s / count +} + +// MeanBy return the mean of seq by fn. +// +// EXAMPLE: +// +// mean := MeanBy(FromSlice([]int{1, 2, 3, 4, 5}), func(v int) int { +// return v * 2 +// }) +// // mean 👉 6 +func MeanBy[T any, R constraints.Number](in Seq[T], fn func(T) R) R { + var count R = 0 + s := Reduce(func(sum R, v T) R { + count++ + return sum + fn(v) + }, 0, in) + return s / count +} + +// Moderate return the most common element in seq. +// +// EXAMPLE: +// +// moderate := Moderate(FromSlice([]int{1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 6})) +// // moderate 👉 6 +func Moderate[T comparable](in Seq[T]) (T, bool) { + var maxTimes int + var result T + _ = Reduce(func(sum map[T]int, v T) map[T]int { + sum[v]++ + if sum[v] > maxTimes { + maxTimes = sum[v] + result = v + } + return sum + }, make(map[T]int), in) + return result, maxTimes > 0 +} + +// ModerateO return the most common element in seq. +// +// EXAMPLE: +// +// moderate := ModerateO(FromSlice([]int{1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 6})) +// // moderate 👉 6 +func ModerateO[T constraints.Number](in Seq[T]) optional.O[T] { + return optional.FromValue2(Moderate(in)) +} diff --git a/pkg/xiter/xiter_common_test.go b/pkg/xiter/xiter_common_test.go index c7a11e6..6c367ff 100644 --- a/pkg/xiter/xiter_common_test.go +++ b/pkg/xiter/xiter_common_test.go @@ -1,6 +1,8 @@ package xiter_test import ( + "sort" + "strconv" "testing" "github.com/dashjay/xiter/pkg/xiter" @@ -81,4 +83,49 @@ func TestXIterCommon(t *testing.T) { assert.Equal(t, _range(0, 5), xiter.ToSlice(onlyLeft)) assert.Equal(t, _range(10, 15), xiter.ToSlice(onlyRight)) }) + + t.Run("intersect", func(t *testing.T) { + left := xiter.FromSlice(_range(0, 10)) + right := xiter.FromSlice(_range(5, 15)) + assert.Equal(t, _range(5, 10), xiter.ToSlice(xiter.Intersect(left, right))) + assert.True(t, xiter.Equal(left, xiter.Intersect(left, left))) + }) + + t.Run("mean", func(t *testing.T) { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + seq := xiter.FromSlice(_range(0, 10)) + r := xiter.Mean(seq) + assert.Equal(t, xiter.Sum(seq)/len(_range(0, 10)), r) + }) + + t.Run("mean by", func(t *testing.T) { + strSeq := xiter.Map(func(in int) string { + return strconv.Itoa(in) + }, xiter.FromSlice(_range(0, 10))) + + r := xiter.MeanBy(strSeq, func(t string) int { + v, _ := strconv.Atoi(t) + return v + }) + assert.Equal(t, xiter.Sum(xiter.FromSlice(_range(0, 10)))/len(_range(0, 10)), r) + }) + t.Run("moderate", func(t *testing.T) { + moderate := xiter.ModerateO(xiter.FromSlice([]int{1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 6})) + assert.True(t, moderate.Ok()) + assert.Equal(t, 6, moderate.Must()) + }) + + t.Run("union", func(t *testing.T) { + left := xiter.FromSlice(_range(0, 10)) + right := xiter.FromSlice(_range(5, 15)) + x := xiter.ToSlice(xiter.Union(left, right)) + sort.Sort(sort.IntSlice(x)) + assert.Equal(t, _range(0, 15), x) + + ut := func(int) struct{} { + return struct{}{} + } + assert.Equal(t, xiter.ToMapFromSeq(xiter.Union(left, right), ut), + xiter.ToMapFromSeq(xiter.Union(right, left), ut)) + }) } diff --git a/pkg/xiter/xiter_test.go b/pkg/xiter/xiter_test.go index 288b02d..63c4cec 100644 --- a/pkg/xiter/xiter_test.go +++ b/pkg/xiter/xiter_test.go @@ -416,6 +416,7 @@ func TestXIter(t *testing.T) { t.Run("compact", func(t *testing.T) { seq := xiter.Compact(xiter.FromSlice([]int{0, 1, 2, 3, 4})) + testLimit(t, seq, 1) assert.Equal(t, []int{1, 2, 3, 4}, xiter.ToSlice(seq)) }) } diff --git a/pkg/xslice/README.md b/pkg/xslice/README.md index 88b5671..07395fc 100644 --- a/pkg/xslice/README.md +++ b/pkg/xslice/README.md @@ -61,6 +61,7 @@ import "github.com/dashjay/xiter/pkg/xslice" - [func Sum\[T constraints.Number, Slice \~\[\]T\]\(in Slice\) T](<#Sum>) - [func SumBy\[T any, R constraints.Number, Slice \~\[\]T\]\(in Slice, f func\(T\) R\) R](<#SumBy>) - [func SumN\[T constraints.Number\]\(in ...T\) T](<#SumN>) +- [func Union\[T comparable, Slice \~\[\]T\]\(left, right Slice\) Slice](<#Union>) - [func Uniq\[T comparable, Slice \~\[\]T\]\(in Slice\) Slice](<#Uniq>) @@ -545,13 +546,22 @@ xslice.Index([]int{1, 2, 3, 4, 5}, 666) 👉 -1 ``` -## func [Intersect]() +## func [Intersect]() ```go func Intersect[T comparable, Slice ~[]T](left, right Slice) Slice ``` +Intersect returns a slice that contains the elements that are in both left and right slices. +EXAMPLE: + +``` +left := []int{1, 2, 3, 4, 5} +right := []int{4, 5, 6, 7, 8} +intersect := xslice.Intersect(left, right) +fmt.Println(intersect) // [4 5] +``` ## func [Join]() @@ -915,6 +925,24 @@ xslice.SumN(1, 2, 3) 👉 6 xslice.SumN() 👉 0 ``` + +## func [Union]() + +```go +func Union[T comparable, Slice ~[]T](left, right Slice) Slice +``` + +Union returns a slice that contains all elements in left and right slices. + +EXAMPLE: + +``` +left := []int{1, 2, 3, 4} +right := []int{3, 4, 5, 6} +union := xslice.Union(left, right) +fmt.Println(union) // [1 2 3 4 5 6] +``` + ## func [Uniq]() diff --git a/pkg/xslice/xslice.go b/pkg/xslice/xslice.go index 44f06c9..6644b18 100644 --- a/pkg/xslice/xslice.go +++ b/pkg/xslice/xslice.go @@ -641,6 +641,14 @@ func Difference[T comparable, Slice ~[]T](left, right Slice) (onlyLeft, onlyRigh return xiter.ToSlice(onlyLeftSeq), xiter.ToSlice(onlyRightSeq) } +// Intersect returns a slice that contains the elements that are in both left and right slices. +// +// EXAMPLE: +// +// left := []int{1, 2, 3, 4, 5} +// right := []int{4, 5, 6, 7, 8} +// intersect := xslice.Intersect(left, right) +// fmt.Println(intersect) // [4 5] func Intersect[T comparable, Slice ~[]T](left, right Slice) Slice { var smaller, larger Slice if len(left) > len(right) { @@ -648,14 +656,23 @@ func Intersect[T comparable, Slice ~[]T](left, right Slice) Slice { } else { smaller, larger = left, right } - smallerMap := xiter.ToMapFromSeq(xiter.FromSlice(smaller), func(T) struct{} { - return struct{}{} - }) + return xiter.ToSlice(xiter.Intersect(xiter.FromSlice(smaller), xiter.FromSlice(larger))) +} - return xiter.ToSlice( - xiter.Filter(func(v T) bool { - _, exists := smallerMap[v] - return exists - }, xiter.FromSlice(larger)), - ) +// Union returns a slice that contains all elements in left and right slices. +// +// EXAMPLE: +// +// left := []int{1, 2, 3, 4} +// right := []int{3, 4, 5, 6} +// union := xslice.Union(left, right) +// fmt.Println(union) // [1 2 3 4 5 6] +func Union[T comparable, Slice ~[]T](left, right Slice) Slice { + var smaller, larger Slice + if len(left) <= len(right) { + smaller, larger = left, right + } else { + smaller, larger = right, left + } + return xiter.ToSlice(xiter.Union(xiter.FromSlice(smaller), xiter.FromSlice(larger))) } diff --git a/pkg/xslice/xslice_test.go b/pkg/xslice/xslice_test.go index ee0e0b0..597b304 100644 --- a/pkg/xslice/xslice_test.go +++ b/pkg/xslice/xslice_test.go @@ -1,6 +1,7 @@ package xslice_test import ( + "sort" "strconv" "testing" @@ -356,4 +357,11 @@ func TestSlices(t *testing.T) { assert.Equal(t, left, xslice.Intersect(left, left)) }) + t.Run("union", func(t *testing.T) { + left := []int{1, 2, 3, 4, 5, 6} + right := []int{4, 5, 6, 7, 8} + res := xslice.Union(left, right) + sort.Sort(sort.IntSlice(res)) + assert.Equal(t, []int{1, 2, 3, 4, 5, 6, 7, 8}, res) + }) } From 3618fb8a86ce8689244d296abc65dee1c219d8d2 Mon Sep 17 00:00:00 2001 From: dashjay Date: Sat, 26 Jul 2025 20:33:52 +0800 Subject: [PATCH 8/8] test --- pkg/xstl/list/example_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/xstl/list/example_test.go b/pkg/xstl/list/example_test.go index a2c8817..66fb62e 100644 --- a/pkg/xstl/list/example_test.go +++ b/pkg/xstl/list/example_test.go @@ -6,7 +6,7 @@ package list_test import ( "fmt" - + "github.com/dashjay/xiter/pkg/xstl/list" )