From 3e740ba931ddb58ca4098eebfe6e33fb35b50ce1 Mon Sep 17 00:00:00 2001 From: Samuel Berthe Date: Thu, 24 Feb 2022 21:28:44 +0100 Subject: [PATCH] feat(uniqby): adding _.uniqBy() style function --- README.rst | 23 +++++++++++++++++++++++ transform.go | 38 ++++++++++++++++++++++++++++++++++++++ transform_test.go | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/README.rst b/README.rst index eada626..818fc7d 100644 --- a/README.rst +++ b/README.rst @@ -654,6 +654,29 @@ see also, typesafe implementations: UniqInt_, UniqInt64_, UniqFloat32_, UniqFloa .. _UniqInt64: https://godoc.org/github.com/thoas/go-funk#UniqInt64 .. _UniqString: https://godoc.org/github.com/thoas/go-funk#UniqString +funk.UniqBy +......... + +Creates an array with unique values returned by a callback. + +.. code-block:: go + + funk.UniqBy([]int{0, 1, 1, 2, 3, 0, 0, 12}, func(nbr int) int { + return nbr % 3 + }) // []int{0, 1, 2} + + foo1 := Foo{ + ID: 42, + FirstName: "Bob", + } + foo2 := Foo{ + ID: 42, + FirstName: "Bob", + } + funk.UniqBy([]Foo{foo1, foo2}, func(f Foo) int { + return f.ID + }) // []Foo{ Foo{ID: 42, Firstname: "Bob"} } + funk.Drop ......... diff --git a/transform.go b/transform.go index 6e35431..73ae76a 100644 --- a/transform.go +++ b/transform.go @@ -412,6 +412,44 @@ func Uniq(in interface{}) interface{} { panic(fmt.Sprintf("Type %s is not supported by Uniq", valueType.String())) } +// Uniq creates an array with unique values. +func UniqBy(in interface{}, mapFunc interface{}) interface{} { + if !IsFunction(mapFunc) { + panic("Second argument must be function") + } + + value := reflect.ValueOf(in) + valueType := value.Type() + + kind := value.Kind() + + funcValue := reflect.ValueOf(mapFunc) + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + result := makeSlice(value, 0) + + seen := make(map[interface{}]bool, length) + + for i := 0; i < length; i++ { + val := value.Index(i) + v := funcValue.Call([]reflect.Value{val})[0].Interface() + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = true + result = reflect.Append(result, val) + } + + return result.Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Uniq", valueType.String())) +} + // ConvertSlice converts a slice type to another, // a perfect example would be to convert a slice of struct to a slice of interface. func ConvertSlice(in interface{}, out interface{}) { diff --git a/transform_test.go b/transform_test.go index 3ed4004..97e5595 100644 --- a/transform_test.go +++ b/transform_test.go @@ -256,6 +256,41 @@ func TestUniq(t *testing.T) { is.Equal(results, []string{"foo", "bar"}) } +func TestUniqBy(t *testing.T) { + is := assert.New(t) + + results := UniqBy([]int{0, 1, 1, 2, 3, 0, 0, 12}, func(nbr int) int { + return nbr % 3 + }) + fmt.Println(results) + is.Len(results, 3) + is.Equal(results, []int{0, 1, 2}) + + type foobar struct { + foo string + bar string + } + + foobar1 := foobar{ + foo: "foo", + bar: "bar", + } + foobar2 := foobar{ + foo: "foo", + bar: "baz", + } + foobar3 := foobar{ + foo: "foo", + bar: "bar", + } + + results = UniqBy([]foobar{foobar1, foobar2, foobar3}, func(f foobar) string { + return f.foo + f.bar + }) + is.Len(results, 2) + is.Equal(results, []foobar{foobar1, foobar2}) +} + func TestConvertSlice(t *testing.T) { instances := []*Foo{foo, foo2}