Skip to content

Commit

Permalink
Merge pull request #142 from samber/feat-uniq-by
Browse files Browse the repository at this point in the history
Adding _.uniqBy() function
  • Loading branch information
thoas committed Mar 9, 2022
2 parents d8341f9 + 3e740ba commit 38b2794
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 0 deletions.
23 changes: 23 additions & 0 deletions README.rst
Expand Up @@ -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
.........

Expand Down
38 changes: 38 additions & 0 deletions transform.go
Expand Up @@ -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{}) {
Expand Down
35 changes: 35 additions & 0 deletions transform_test.go
Expand Up @@ -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}

Expand Down

0 comments on commit 38b2794

Please sign in to comment.