From a3bc20af1d88e7349452c5f61d5208ecd5267e03 Mon Sep 17 00:00:00 2001 From: dudaodong Date: Tue, 17 Jan 2023 11:39:05 +0800 Subject: [PATCH] feat: add Distinct --- stream/stream.go | 35 ++++++++++++++++++++++++++++++++++- stream/stream_test.go | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/stream/stream.go b/stream/stream.go index 35c9d9d..d2f789e 100644 --- a/stream/stream.go +++ b/stream/stream.go @@ -6,7 +6,12 @@ // powerful like other libs package stream -import "golang.org/x/exp/constraints" +import ( + "bytes" + "encoding/gob" + + "golang.org/x/exp/constraints" +) // A stream should implements methods: // type StreamI[T any] interface { @@ -69,6 +74,34 @@ func FromRange[T constraints.Integer | constraints.Float](start, end, step T) st return stream[T]{source: source} } +// Distinct returns a stream that removes the duplicated items. +func (s stream[T]) Distinct() stream[T] { + source := make([]T, 0) + + distinct := map[string]bool{} + + for _, v := range s.source { + // todo: performance issue + k := hashKey(v) + if _, ok := distinct[k]; !ok { + distinct[k] = true + source = append(source, v) + } + } + + return FromSlice(source) +} + +func hashKey(data any) string { + buffer := bytes.NewBuffer(nil) + encoder := gob.NewEncoder(buffer) + err := encoder.Encode(data) + if err != nil { + panic("stream.hashKey: get hashkey failed") + } + return buffer.String() +} + // ToSlice return the elements in the stream. func (s stream[T]) ToSlice() []T { return s.source diff --git a/stream/stream_test.go b/stream/stream_test.go index d15dac1..3b91dbd 100644 --- a/stream/stream_test.go +++ b/stream/stream_test.go @@ -15,7 +15,7 @@ func TestFromSlice(t *testing.T) { } func TestFromRange(t *testing.T) { - assert := internal.NewAssert(t, "TestFromSlice") + assert := internal.NewAssert(t, "TestFromRange") s1 := FromRange(1, 5, 1) s2 := FromRange(1.1, 5.0, 1.0) @@ -23,3 +23,35 @@ func TestFromRange(t *testing.T) { assert.Equal([]int{1, 2, 3, 4, 5}, s1.ToSlice()) assert.Equal([]float64{1.1, 2.1, 3.1, 4.1}, s2.ToSlice()) } + +func TestStream_Distinct(t *testing.T) { + assert := internal.NewAssert(t, "TestStream_Distinct") + + nums := FromSlice([]int{1, 2, 2, 3, 3, 3}) + distinctNums := nums.Distinct() + + assert.Equal([]int{1, 2, 2, 3, 3, 3}, nums.ToSlice()) + assert.Equal([]int{1, 2, 3}, distinctNums.ToSlice()) + + type Person struct { + Id string + Name string + Age uint + } + + people := []Person{ + {Id: "001", Name: "Tom", Age: 10}, + {Id: "001", Name: "Tom", Age: 10}, + {Id: "002", Name: "Jim", Age: 20}, + {Id: "003", Name: "Mike", Age: 30}, + } + + stream := FromSlice(people) + distinctStream := stream.Distinct() + + // {[{001 Tom 10} {001 Tom 10} {002 Jim 20} {003 Mike 30}]} + t.Log(stream) + + // {[{001 Tom 10} {002 Jim 20} {003 Mike 30}]} + t.Log(distinctStream) +}