package slice import ( "hash/fnv" "math/rand" "sort" "strings" "github.com/ipfs/go-cid" "github.com/samber/lo" "golang.org/x/exp/constraints" "golang.org/x/exp/slices" ) func Union(a, b []string) []string { set := make(map[string]struct{}, len(a)) for _, v := range a { set[v] = struct{}{} } for _, v := range b { if _, ok := set[v]; !ok { a = append(a, v) } } return a } func DifferenceRemovedAdded(a, b []string) (removed []string, added []string) { var amap = map[string]struct{}{} var bmap = map[string]struct{}{} for _, item := range a { amap[item] = struct{}{} } for _, item := range b { if _, exists := amap[item]; !exists { added = append(added, item) } bmap[item] = struct{}{} } for _, item := range a { if _, exists := bmap[item]; !exists { removed = append(removed, item) } } return } func FindPos[T comparable](s []T, v T) int { for i, sv := range s { if sv == v { return i } } return -1 } func Find[T comparable](s []T, cond func(T) bool) int { for i, sv := range s { if cond(sv) { return i } } return -1 } // Difference returns the elements in `a` that aren't in `b`. func Difference(a, b []string) []string { var diff = make([]string, 0, len(a)) for _, a1 := range a { if FindPos(b, a1) == -1 { diff = append(diff, a1) } } return diff } func Insert[T any](s []T, pos int, v ...T) []T { if len(s) <= pos { return append(s, v...) } if pos == 0 { return append(v, s[pos:]...) } return append(s[:pos], append(v, s[pos:]...)...) } // RemoveMut reuses provided slice capacity. Provided s slice should not be used after without reassigning to the func return! func RemoveMut[T comparable](s []T, v T) []T { var n int for _, x := range s { if x != v { s[n] = x n++ } } return s[:n] } // Remove is an immutable analog of RemoveMut function. Input slice is copied and then modified func Remove[T comparable](s []T, v T) []T { var n int sc := slices.Clone(s) for _, x := range s { if x != v { sc[n] = x n++ } } return sc[:n] } // RemoveIndex reuses provided slice capacity. Provided s slice should not be used after without reassigning to the func return! func RemoveIndex[T any](s []T, idx int) []T { var n int for i, x := range s { if i != idx { s[n] = x n++ } } return s[:n] } // RemoveN is an analog of Remove function, but removing a range of values func RemoveN[T comparable](s []T, vs ...T) []T { var ( n int found bool sc = slices.Clone(s) ) if len(vs) == 0 { return sc } for _, x := range s { found = false for _, v := range vs { if x == v { found = true break } } if !found { sc[n] = x n++ } } return sc[:n] } func Filter[T any](vals []T, cond func(T) bool) []T { var result = make([]T, 0, len(vals)) for i := range vals { if cond(vals[i]) { result = append(result, vals[i]) } } return result } func FilterMut[T any](vals []T, cond func(T) bool) []T { result := vals[:0] for i := range vals { if cond(vals[i]) { result = append(result, vals[i]) } } return result } func GetRandomString(s []string, seed string) string { rand.Seed(int64(hash(seed))) return s[rand.Intn(len(s))] } func hash(s string) uint64 { h := fnv.New64a() h.Write([]byte(s)) return h.Sum64() } func SortedEquals[K constraints.Ordered](s1, s2 []K) bool { if len(s1) != len(s2) { return false } for i := range s1 { if s1[i] != s2[i] { return false } } return true } func UnsortedEqual[K constraints.Ordered](s1, s2 []K) bool { if len(s1) != len(s2) { return false } s1Sorted := make([]K, len(s1)) s2Sorted := make([]K, len(s2)) copy(s1Sorted, s1) copy(s2Sorted, s2) slices.Sort(s1Sorted) slices.Sort(s2Sorted) return SortedEquals(s1Sorted, s2Sorted) } func HasPrefix(value, prefix []string) bool { if len(value) < len(prefix) { return false } for i, p := range prefix { if value[i] != p { return false } } return true } func Copy[T any](list []T) []T { newList := make([]T, len(list)) copy(newList, list) return newList } func Intersection(a, b []string) (res []string) { sort.Strings(a) sort.Strings(b) aIdx := 0 bIdx := 0 for aIdx < len(a) && bIdx < len(b) { cmp := strings.Compare(a[aIdx], b[bIdx]) switch cmp { case 0: res = append(res, a[aIdx]) aIdx++ bIdx++ case -1: aIdx++ case 1: bIdx++ } } return } func ReplaceFirstBy[T comparable](s []T, el T, pred func(el T) bool) []T { for i, el2 := range s { if pred(el2) { s[i] = el break } } return s } func FilterCID(cids []string) []string { return lo.Filter(cids, func(item string, index int) bool { _, err := cid.Parse(item) return err == nil }) } func StringsInto[T ~string](from []string) []T { to := make([]T, len(from)) for i, v := range from { to[i] = T(v) } return to } func IntoStrings[T ~string](from []T) []string { to := make([]string, len(from)) for i, v := range from { to[i] = string(v) } return to } type numeric interface { constraints.Integer | constraints.Float } func FloatsInto[T numeric](from []float64) []T { to := make([]T, len(from)) for i, v := range from { to[i] = T(v) } return to } // MergeUniqBy merges two slices with comparator. Resulting slice saves values' order and uniqueness. // Input slices MUST contain only unique values func MergeUniqBy[T comparable](s1, s2 []T, equal func(v1, v2 T) bool) (result []T) { result = make([]T, len(s1)) copy(result, s1) for _, v2 := range s2 { if !slices.ContainsFunc(s1, func(v1 T) bool { return equal(v1, v2) }) { result = append(result, v2) } } return result }