Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions pkg/internal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>)


<a name="IsZero"></a>
## func [IsZero](<https://github.com/dashjay/xiter/blob/main/pkg/internal/utils/utils.go#L3>)

```go
func IsZero[T comparable](v T) bool
```



# xassert

```go
Expand Down
6 changes: 6 additions & 0 deletions pkg/internal/utils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package utils

func IsZero[T comparable](v T) bool {
var zero T
return v == zero
}
370 changes: 294 additions & 76 deletions pkg/xiter/README.md

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions pkg/xiter/xiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -478,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 {
Expand All @@ -488,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 {
Expand All @@ -502,6 +506,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))
}
Expand Down Expand Up @@ -1030,3 +1042,78 @@ 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
for t := range in {
v = t
ok = true
break
}
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
for t := range in {
v = t
ok = true
}
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))
}

// 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
}
}
}
}
127 changes: 127 additions & 0 deletions pkg/xiter/xiter_common.go
Original file line number Diff line number Diff line change
@@ -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"
)
Expand Down Expand Up @@ -111,3 +112,129 @@ 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)
}

// 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))
}
58 changes: 57 additions & 1 deletion pkg/xiter/xiter_common_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package xiter_test

import (
"sort"
"strconv"
"testing"

"github.com/dashjay/xiter/pkg/xiter"
"github.com/stretchr/testify/assert"
"testing"
)

func TestXIterCommon(t *testing.T) {
Expand Down Expand Up @@ -72,4 +75,57 @@ 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))
})

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))
})
}
Loading