github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/utils/slices.go (about) 1 /* 2 Copyright 2021 Gravitational, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package utils 18 19 import ( 20 "slices" 21 "strings" 22 ) 23 24 // JoinStrings returns a string that is all the elements in the slice `T[]` joined by `sep` 25 // This being generic allows for the usage of custom string times, without having to convert 26 // the elements to a string to be passed into `strings.Join`. 27 func JoinStrings[T ~string](elems []T, sep string) T { 28 switch len(elems) { 29 case 0: 30 return "" 31 case 1: 32 return elems[0] 33 } 34 n := len(sep) * (len(elems) - 1) 35 for i := 0; i < len(elems); i++ { 36 n += len(elems[i]) 37 } 38 39 var b strings.Builder 40 b.Grow(n) 41 b.WriteString(string(elems[0])) 42 for _, s := range elems[1:] { 43 b.WriteString(sep) 44 b.WriteString(string(s)) 45 } 46 return T(b.String()) 47 } 48 49 // Deduplicate deduplicates list of comparable values. 50 func Deduplicate[T comparable](in []T) []T { 51 if len(in) <= 1 { 52 return in 53 } 54 out := make([]T, 0, len(in)) 55 seen := make(map[T]struct{}, len(in)) 56 for _, val := range in { 57 if _, ok := seen[val]; !ok { 58 out = append(out, val) 59 seen[val] = struct{}{} 60 } 61 } 62 return out 63 } 64 65 // DeduplicateAny deduplicates list of any values with compare function. 66 func DeduplicateAny[T any](in []T, compare func(T, T) bool) []T { 67 if len(in) <= 1 { 68 return in 69 } 70 out := make([]T, 0, len(in)) 71 for _, val := range in { 72 var seen bool 73 for _, outVal := range out { 74 if compare(val, outVal) { 75 seen = true 76 break 77 } 78 } 79 if !seen { 80 out = append(out, val) 81 } 82 } 83 return out 84 } 85 86 // ContainSameUniqueElements returns true if the input slices contain the same 87 // unique elements. Ordering and duplicates are ignored. 88 func ContainSameUniqueElements[S ~[]E, E comparable](s1, s2 S) bool { 89 s1Dedup := Deduplicate(s1) 90 s2Dedup := Deduplicate(s2) 91 92 if len(s1Dedup) != len(s2Dedup) { 93 return false 94 } 95 for i := range s1Dedup { 96 if !slices.Contains(s2Dedup, s1Dedup[i]) { 97 return false 98 } 99 } 100 return true 101 } 102 103 // Any checks if any element of slice satisfy given predicate. If the slice is empty, it returns false. 104 func Any[S ~[]E, E any](s S, predicate func(E) bool) bool { 105 for _, e := range s { 106 if predicate(e) { 107 return true 108 } 109 } 110 return false 111 } 112 113 // All checks if all elements of slice satisfy given predicate. If the slice is empty, it returns true. 114 func All[S ~[]E, E any](s S, predicate func(E) bool) bool { 115 for _, e := range s { 116 if !predicate(e) { 117 return false 118 } 119 } 120 return true 121 } 122 123 // CountBy counts the occurrences of each element in a slice based on a given mapper function. 124 func CountBy[S ~[]E, E any](elements S, mapper func(E) string) map[string]int { 125 out := make(map[string]int) 126 for _, elem := range elements { 127 key := mapper(elem) 128 out[key] += 1 129 } 130 return out 131 }