diff --git a/helpers/reflect.go b/helpers/reflect.go index e501171..d9d964b 100644 --- a/helpers/reflect.go +++ b/helpers/reflect.go @@ -76,8 +76,8 @@ func DereferencePointer(val interface{}) interface{} { } /* - TODO: add support for multi-dimensional arrays - like - arr[1][2] arr[1][2][3] and so on.. + TODO: add support for multi-dimensional arrays + like - arr[1][2] arr[1][2][3] and so on.. */ func IsFieldArray(fieldName string) (string, int, bool) { r := regexp.MustCompile(`^(.*)\[(\d+)\]$`) @@ -146,3 +146,28 @@ func ExecuteMethod(item interface{}, methodName string) (returnValues []interfac } return } + +// StructToMap uses reflection to convert a struct to a map +func StructToMap(s any) map[string]any { + result := make(map[string]any) + val := reflect.ValueOf(s) + + // We only accept structs + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + + if val.Kind() != reflect.Struct { + fmt.Println("Expecting a struct") + return nil + } + + for i := 0; i < val.NumField(); i++ { + field := val.Type().Field(i) + value := val.Field(i).Interface() + + result[field.Name] = value + } + + return result +} diff --git a/helpers/slice.go b/helpers/slice.go index 12caffd..f1fc1da 100644 --- a/helpers/slice.go +++ b/helpers/slice.go @@ -49,3 +49,21 @@ func ToTypedSlice[T any](input []any) []T { } return res } + +// AppendSliceUnique appends elements from slice2 to slice1, omitting duplicates. +func AppendSliceUnique[T comparable](slice1, slice2 []T) []T { + // Map existing elements of slice1 for quick lookup + exists := SliceToLookup(slice1) + result := make([]T, len(slice1)) + copy(result, slice1) + + // Check each element in slice2; if not a duplicate, append it to the result + for _, item := range slice2 { + if _, dupe := exists[item]; !dupe { + result = append(result, item) + exists[item] = struct{}{} + } + } + + return result +} diff --git a/helpers/slice_test.go b/helpers/slice_test.go new file mode 100644 index 0000000..99403c0 --- /dev/null +++ b/helpers/slice_test.go @@ -0,0 +1,55 @@ +package helpers + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestAppendUnique(t *testing.T) { + type args[T comparable] struct { + slice1 []T + slice2 []T + } + type testCase[T comparable] struct { + name string + args args[T] + want []T + } + tests := []testCase[string]{ + { + name: "empty slices", + args: args[string]{slice1: []string{}, slice2: []string{}}, + want: []string{}, + }, + { + name: "empty slice1", + args: args[string]{slice1: []string{}, slice2: []string{"a", "b"}}, + want: []string{"a", "b"}, + }, + { + name: "empty slice2", + args: args[string]{slice1: []string{"a", "b"}, slice2: []string{}}, + want: []string{"a", "b"}, + }, + { + name: "no duplicates", + args: args[string]{slice1: []string{"a", "b"}, slice2: []string{"c", "d"}}, + want: []string{"a", "b", "c", "d"}, + }, + { + name: "duplicates", + args: args[string]{slice1: []string{"a", "b"}, slice2: []string{"b", "c"}}, + want: []string{"a", "b", "c"}, + }, + { + name: "duplicates in both slices", + args: args[string]{slice1: []string{"a", "b", "c"}, slice2: []string{"b", "c", "d"}}, + want: []string{"a", "b", "c", "d"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, AppendSliceUnique(tt.args.slice1, tt.args.slice2), "AppendSliceUnique(%v, %v)", tt.args.slice1, tt.args.slice2) + }) + } +}