// Package structs provides some utility functions for dealing with structs. package structs import ( "maps" "slices" orderedmap "github.com/wk8/go-ordered-map" ) // CopyOrZeroValue returns a copy of s if s is not nil otherwise the zero value of T will be returned. func CopyOrZeroValue[T any](s *T) *T { cp := new(T) if s != nil { *cp = *s } return cp } // Returns a copy of an array with a unique set of elements. // // Element order is retained. func Uniq[T comparable](source []T) []T { m := orderedmap.New() for _, v := range source { m.Set(v, true) } set := make([]T, m.Len()) i := 0 for pair := m.Oldest(); pair != nil; pair = pair.Next() { set[i] = pair.Key.(T) i++ } return set } func Keys[K comparable, V any](source map[K]V) []K { if source == nil { var zero []K return zero } return slices.Collect(maps.Keys(source)) } func Index[K comparable, V any](source []V, indexer func(V) K) map[K]V { if source == nil { var zero map[K]V return zero } result := map[K]V{} for _, v := range source { k := indexer(v) result[k] = v } return result } func Map[E any, R any](source []E, mapper func(E) R) []R { if source == nil { var zero []R return zero } result := make([]R, len(source)) for i, e := range source { result[i] = mapper(e) } return result } func MapValues[K comparable, S any, T any](m map[K]S, mapper func(S) T) map[K]T { r := make(map[K]T, len(m)) for k, s := range m { r[k] = mapper(s) } return r } func MapValues2[K comparable, S any, T any](m map[K]S, mapper func(K, S) T) map[K]T { r := make(map[K]T, len(m)) for k, s := range m { r[k] = mapper(k, s) } return r } func MapKeys[S comparable, T comparable, V any](m map[S]V, mapper func(S) T) map[T]V { r := make(map[T]V, len(m)) for s, v := range m { r[mapper(s)] = v } return r } func MapKeys2[S comparable, T comparable, V any](m map[S]V, mapper func(S, V) T) map[T]V { r := make(map[T]V, len(m)) for s, v := range m { r[mapper(s, v)] = v } return r } func ToBoolMap[E comparable](source []E) map[E]bool { m := make(map[E]bool, len(source)) for _, v := range source { m[v] = true } return m } func ToIntMap[E comparable](source []E) map[E]int { m := make(map[E]int, len(source)) for _, v := range source { if e, ok := m[v]; ok { m[v] = e + 1 } else { m[v] = 1 } } return m } func MapN[E any, R any](source []E, indexer func(E) *R) []R { if source == nil { var zero []R return zero } result := []R{} for _, e := range source { opt := indexer(e) if opt != nil { result = append(result, *opt) } } return result } // Check whether two slices contain the same elements, ignoring order. func SameSlices[E comparable](x, y []E) bool { // https://stackoverflow.com/a/36000696 if len(x) != len(y) { return false } // create a map of string -> int diff := make(map[E]int, len(x)) for _, _x := range x { // 0 value for int is 0, so just increment a counter for the string diff[_x]++ } for _, _y := range y { // If the string _y is not in diff bail out early if _, ok := diff[_y]; !ok { return false } diff[_y]-- if diff[_y] == 0 { delete(diff, _y) } } return len(diff) == 0 } func Missing[E comparable](expected, actual []E) []E { missing := []E{} actualIndex := ToBoolMap(actual) for _, e := range expected { if _, ok := actualIndex[e]; !ok { missing = append(missing, e) } } return missing } func FirstKey[K comparable, V any](m map[K]V) (K, bool) { for k := range m { return k, true } var zero K return zero, false } func Any[E any](s []E, predicate func(E) bool) bool { if len(s) < 1 { return false } for _, e := range s { if predicate(e) { return true } } return false } func AnyKey[K comparable, V any](m map[K]V, predicate func(K) bool) bool { if len(m) < 1 { return false } for k := range m { if predicate(k) { return true } } return false } func AnyValue[K comparable, V any](m map[K]V, predicate func(V) bool) bool { if len(m) < 1 { return false } for _, v := range m { if predicate(v) { return true } } return false } func AnyItem[K comparable, V any](m map[K]V, predicate func(K, V) bool) bool { if len(m) < 1 { return false } for k, v := range m { if predicate(k, v) { return true } } return false } func Concat[E any](arys ...[]E) []E { l := 0 for _, ary := range arys { l += len(ary) } r := make([]E, l) i := 0 for _, ary := range arys { if ary != nil { i += copy(r[i:], ary) } } return r } func Filter[E any](s []E, predicate func(E) bool) []E { r := []E{} for _, e := range s { if predicate(e) { r = append(r, e) } } return r }