Skip to content

Commit 4d92c42

Browse files
committed
implement difference
1 parent cc2bc83 commit 4d92c42

9 files changed

Lines changed: 188 additions & 43 deletions

File tree

pkg/xiter/README.md

Lines changed: 72 additions & 42 deletions
Large diffs are not rendered by default.

pkg/xiter/xiter.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,14 @@ func ToMap[K comparable, V any](seq Seq2[K, V]) (out map[K]V) {
504504
return maps.Collect(iter.Seq2[K, V](seq))
505505
}
506506

507+
func ToMapFromSeq[K comparable, V any](seq Seq[K], fn func(k K) V) (out map[K]V) {
508+
out = make(map[K]V)
509+
for k := range seq {
510+
out[k] = fn(k)
511+
}
512+
return out
513+
}
514+
507515
func FromMapKeys[K comparable, V any](m map[K]V) Seq[K] {
508516
return Seq[K](maps.Keys(m))
509517
}

pkg/xiter/xiter_common.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,31 @@ func FromChan[T any](in <-chan T) Seq[T] {
111111
}
112112
}
113113
}
114+
115+
// Difference returns two sequences: the first sequence contains elements that are in the left sequence but not in the right sequence,
116+
// and the second sequence contains elements that are in the right sequence but not in the left sequence.
117+
//
118+
// EXAMPLE:
119+
//
120+
// left := []int{1, 2, 3, 4}
121+
// right := []int{3, 4, 5, 6}
122+
// onlyLeft, onlyRight := Difference(FromSlice(left), FromSlice(right))
123+
// // onlyLeft 👉 [1 2]
124+
// // onlyRight 👉 [5 6]
125+
func Difference[T comparable](left Seq[T], right Seq[T]) (onlyLeft Seq[T], onlyRight Seq[T]) {
126+
leftMap := ToMapFromSeq(left, func(k T) struct{} {
127+
return struct{}{}
128+
})
129+
rightMap := ToMapFromSeq(right, func(k T) struct{} {
130+
return struct{}{}
131+
})
132+
133+
return Filter(func(v T) bool {
134+
_, ok := rightMap[v]
135+
return !ok
136+
}, left),
137+
Filter(func(v T) bool {
138+
_, ok := leftMap[v]
139+
return !ok
140+
}, right)
141+
}

pkg/xiter/xiter_common_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package xiter_test
22

33
import (
4+
"testing"
5+
46
"github.com/dashjay/xiter/pkg/xiter"
57
"github.com/stretchr/testify/assert"
6-
"testing"
78
)
89

910
func TestXIterCommon(t *testing.T) {
@@ -72,4 +73,12 @@ func TestXIterCommon(t *testing.T) {
7273
seq := xiter.FromChan(ch)
7374
testLimit(t, seq, 1)
7475
})
76+
77+
t.Run("difference", func(t *testing.T) {
78+
left := xiter.FromSlice(_range(0, 10))
79+
right := xiter.FromSlice(_range(5, 15))
80+
onlyLeft, onlyRight := xiter.Difference(left, right)
81+
assert.Equal(t, _range(0, 5), xiter.ToSlice(onlyLeft))
82+
assert.Equal(t, _range(10, 15), xiter.ToSlice(onlyRight))
83+
})
7584
}

pkg/xiter/xiter_old.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,15 @@ func ToMap[K comparable, V any](seq Seq2[K, V]) (out map[K]V) {
349349
return out
350350
}
351351

352+
func ToMapFromSeq[K comparable, V any](seq Seq[K], fn func(k K) V) (out map[K]V) {
353+
out = make(map[K]V)
354+
seq(func(k K) bool {
355+
out[k] = fn(k)
356+
return true
357+
})
358+
return out
359+
}
360+
352361
func FromMapKeys[K comparable, V any](m map[K]V) Seq[K] {
353362
return func(yield func(K) bool) {
354363
for k := range m {

pkg/xslice/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import "github.com/dashjay/xiter/pkg/xslice"
2424
- [func ContainsAny\[T comparable\]\(in \[\]T, v \[\]T\) bool](<#ContainsAny>)
2525
- [func ContainsBy\[T any\]\(in \[\]T, f func\(T\) bool\) bool](<#ContainsBy>)
2626
- [func Count\[T any\]\(in \[\]T\) int](<#Count>)
27+
- [func Difference\[T comparable, Slice \~\[\]T\]\(left, right Slice\) \(onlyLeft, onlyRight Slice\)](<#Difference>)
2728
- [func Filter\[T any, Slice \~\[\]T\]\(in Slice, f func\(T\) bool\) Slice](<#Filter>)
2829
- [func Find\[T any\]\(in \[\]T, f func\(T\) bool\) \(val T, found bool\)](<#Find>)
2930
- [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
327328
xslice.Count([]int{}) 👉 0
328329
```
329330

331+
<a name="Difference"></a>
332+
## func [Difference](<https://github.com/dashjay/xiter/blob/main/pkg/xslice/xslice.go#L639>)
333+
334+
```go
335+
func Difference[T comparable, Slice ~[]T](left, right Slice) (onlyLeft, onlyRight Slice)
336+
```
337+
338+
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.
339+
340+
EXAMPLE:
341+
342+
```
343+
left := []int{1, 2, 3, 4, 5}
344+
right := []int{4, 5, 6, 7, 8}
345+
onlyLeft, onlyRight := xslice.Difference(left, right)
346+
fmt.Println(onlyLeft) // [1 2 3]
347+
fmt.Println(onlyRight) // [6 7 8]
348+
```
349+
330350
<a name="Filter"></a>
331351
## func [Filter](<https://github.com/dashjay/xiter/blob/main/pkg/xslice/xslice.go#L576>)
332352

pkg/xslice/xslice.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,3 +625,18 @@ func Last[T any, Slice ~[]T](in Slice) (T, bool) {
625625
func LastO[T any, Slice ~[]T](in Slice) optional.O[T] {
626626
return optional.FromValue2(Last(in))
627627
}
628+
629+
// Difference returns two slices: the first slice contains the elements that are in the left slice but not in the right slice,
630+
// and the second slice contains the elements that are in the right slice but not in the left slice.
631+
//
632+
// EXAMPLE:
633+
//
634+
// left := []int{1, 2, 3, 4, 5}
635+
// right := []int{4, 5, 6, 7, 8}
636+
// onlyLeft, onlyRight := xslice.Difference(left, right)
637+
// fmt.Println(onlyLeft) // [1 2 3]
638+
// fmt.Println(onlyRight) // [6 7 8]
639+
func Difference[T comparable, Slice ~[]T](left, right Slice) (onlyLeft, onlyRight Slice) {
640+
onlyLeftSeq, onlyRightSeq := xiter.Difference(xiter.FromSlice(left), xiter.FromSlice(right))
641+
return xiter.ToSlice(onlyLeftSeq), xiter.ToSlice(onlyRightSeq)
642+
}

pkg/xslice/xslice_benchmark_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,21 @@ func BenchmarkSlice(b *testing.B) {
183183
}
184184
})
185185
})
186+
187+
b.Run("benchmark difference", func(b *testing.B) {
188+
left := _range(0, 1000)
189+
right := _range(500, 1500)
190+
b.Run("xslice", func(b *testing.B) {
191+
for i := 0; i < b.N; i++ {
192+
xslice.Difference(left, right)
193+
}
194+
})
195+
b.Run("lo", func(b *testing.B) {
196+
for i := 0; i < b.N; i++ {
197+
lo.Difference(left, right)
198+
}
199+
})
200+
})
186201
}
187202

188203
func BenchmarkChunk(b *testing.B) {

pkg/xslice/xslice_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,9 +331,20 @@ func TestSlices(t *testing.T) {
331331

332332
t.Run("first", func(t *testing.T) {
333333
assert.Equal(t, 1, xslice.FirstO([]int{1, 2, 3}).Must())
334+
assert.False(t, xslice.FirstO([]int{}).Ok())
334335
})
335336

336337
t.Run("last", func(t *testing.T) {
337338
assert.Equal(t, 3, xslice.LastO([]int{1, 2, 3}).Must())
339+
assert.False(t, xslice.LastO([]int{}).Ok())
338340
})
341+
342+
t.Run("difference", func(t *testing.T) {
343+
left := []int{1, 2, 3, 4, 5}
344+
right := []int{4, 5, 6, 7, 8}
345+
onlyLeft, onlyRight := xslice.Difference(left, right)
346+
assert.Equal(t, []int{1, 2, 3}, onlyLeft)
347+
assert.Equal(t, []int{6, 7, 8}, onlyRight)
348+
})
349+
339350
}

0 commit comments

Comments
 (0)