1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-08 05:47:07 +09:00
anytype-heart/util/slice/slice.go
2025-02-25 19:09:13 +03:00

309 lines
5.5 KiB
Go

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
}