1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-04 12:52:28 +08:00

feat: add UniqueByField

This commit is contained in:
dudaodong
2024-06-24 19:36:02 +08:00
parent ca373b00a7
commit 95b516e278
3 changed files with 91 additions and 1 deletions

View File

@@ -796,6 +796,46 @@ func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T {
return Unique(result) return Unique(result)
} }
// UniqueByField remove duplicate elements in struct slice by struct field.
// Play: todo
func UniqueByField[T any](slice []T, field string) ([]T, error) {
seen := map[any]struct{}{}
var result []T
for _, item := range slice {
val, err := getField(item, field)
if err != nil {
return nil, fmt.Errorf("get field %s failed: %v", field, err)
}
if _, ok := seen[val]; !ok {
seen[val] = struct{}{}
result = append(result, item)
}
}
return result, nil
}
func getField[T any](item T, field string) (interface{}, error) {
v := reflect.ValueOf(item)
t := reflect.TypeOf(item)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", item)
}
f := v.FieldByName(field)
if !f.IsValid() {
return nil, fmt.Errorf("field name %s not found", field)
}
return v.FieldByName(field).Interface(), nil
}
// Union creates a slice of unique elements, in order, from all given slices. // Union creates a slice of unique elements, in order, from all given slices.
// Play: https://go.dev/play/p/hfXV1iRIZOf // Play: https://go.dev/play/p/hfXV1iRIZOf
func Union[T comparable](slices ...[]T) []T { func Union[T comparable](slices ...[]T) []T {

View File

@@ -780,6 +780,28 @@ func ExampleUniqueBy() {
// [1 2 0] // [1 2 0]
} }
func ExampleUniqueByField() {
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
users := []User{
{ID: 1, Name: "a"},
{ID: 2, Name: "b"},
{ID: 1, Name: "c"},
}
result, err := UniqueByField(users, "ID")
if err != nil {
}
fmt.Println(result)
// Output:
// [{1 a} {2 b}]
}
func ExampleUnion() { func ExampleUnion() {
nums1 := []int{1, 3, 4, 6} nums1 := []int{1, 3, 4, 6}
nums2 := []int{1, 2, 5, 6} nums2 := []int{1, 2, 5, 6}

View File

@@ -2,11 +2,12 @@ package slice
import ( import (
"fmt" "fmt"
"github.com/duke-git/lancet/v2/internal"
"math" "math"
"reflect" "reflect"
"strconv" "strconv"
"testing" "testing"
"github.com/duke-git/lancet/v2/internal"
) )
func TestContain(t *testing.T) { func TestContain(t *testing.T) {
@@ -735,6 +736,33 @@ func TestUniqueBy(t *testing.T) {
assert.Equal([]int{1, 2, 3, 0}, actual) assert.Equal([]int{1, 2, 3, 0}, actual)
} }
func TestUniqueByField(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUniqueByField")
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
users := []User{
{ID: 1, Name: "a"},
{ID: 2, Name: "b"},
{ID: 1, Name: "c"},
}
uniqueUsers, err := UniqueByField(users, "ID")
if err != nil {
t.Error(err)
}
assert.Equal([]User{
{ID: 1, Name: "a"},
{ID: 2, Name: "b"},
}, uniqueUsers)
}
func TestUnion(t *testing.T) { func TestUnion(t *testing.T) {
t.Parallel() t.Parallel()