slices.go (7427B)
1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package slices defines various functions useful with slices of any type. 6 // Unless otherwise specified, these functions all apply to the elements 7 // of a slice at index 0 <= i < len(s). 8 // 9 // Note that the less function in IsSortedFunc, SortFunc, SortStableFunc requires a 10 // strict weak ordering (https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings), 11 // or the sorting may fail to sort correctly. A common case is when sorting slices of 12 // floating-point numbers containing NaN values. 13 package slices 14 15 import "golang.org/x/exp/constraints" 16 17 // Equal reports whether two slices are equal: the same length and all 18 // elements equal. If the lengths are different, Equal returns false. 19 // Otherwise, the elements are compared in increasing index order, and the 20 // comparison stops at the first unequal pair. 21 // Floating point NaNs are not considered equal. 22 func Equal[E comparable](s1, s2 []E) bool { 23 if len(s1) != len(s2) { 24 return false 25 } 26 for i := range s1 { 27 if s1[i] != s2[i] { 28 return false 29 } 30 } 31 return true 32 } 33 34 // EqualFunc reports whether two slices are equal using a comparison 35 // function on each pair of elements. If the lengths are different, 36 // EqualFunc returns false. Otherwise, the elements are compared in 37 // increasing index order, and the comparison stops at the first index 38 // for which eq returns false. 39 func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool { 40 if len(s1) != len(s2) { 41 return false 42 } 43 for i, v1 := range s1 { 44 v2 := s2[i] 45 if !eq(v1, v2) { 46 return false 47 } 48 } 49 return true 50 } 51 52 // Compare compares the elements of s1 and s2. 53 // The elements are compared sequentially, starting at index 0, 54 // until one element is not equal to the other. 55 // The result of comparing the first non-matching elements is returned. 56 // If both slices are equal until one of them ends, the shorter slice is 57 // considered less than the longer one. 58 // The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2. 59 // Comparisons involving floating point NaNs are ignored. 60 func Compare[E constraints.Ordered](s1, s2 []E) int { 61 s2len := len(s2) 62 for i, v1 := range s1 { 63 if i >= s2len { 64 return +1 65 } 66 v2 := s2[i] 67 switch { 68 case v1 < v2: 69 return -1 70 case v1 > v2: 71 return +1 72 } 73 } 74 if len(s1) < s2len { 75 return -1 76 } 77 return 0 78 } 79 80 // CompareFunc is like Compare but uses a comparison function 81 // on each pair of elements. The elements are compared in increasing 82 // index order, and the comparisons stop after the first time cmp 83 // returns non-zero. 84 // The result is the first non-zero result of cmp; if cmp always 85 // returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2), 86 // and +1 if len(s1) > len(s2). 87 func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int { 88 s2len := len(s2) 89 for i, v1 := range s1 { 90 if i >= s2len { 91 return +1 92 } 93 v2 := s2[i] 94 if c := cmp(v1, v2); c != 0 { 95 return c 96 } 97 } 98 if len(s1) < s2len { 99 return -1 100 } 101 return 0 102 } 103 104 // Index returns the index of the first occurrence of v in s, 105 // or -1 if not present. 106 func Index[E comparable](s []E, v E) int { 107 for i := range s { 108 if v == s[i] { 109 return i 110 } 111 } 112 return -1 113 } 114 115 // IndexFunc returns the first index i satisfying f(s[i]), 116 // or -1 if none do. 117 func IndexFunc[E any](s []E, f func(E) bool) int { 118 for i := range s { 119 if f(s[i]) { 120 return i 121 } 122 } 123 return -1 124 } 125 126 // Contains reports whether v is present in s. 127 func Contains[E comparable](s []E, v E) bool { 128 return Index(s, v) >= 0 129 } 130 131 // ContainsFunc reports whether at least one 132 // element e of s satisfies f(e). 133 func ContainsFunc[E any](s []E, f func(E) bool) bool { 134 return IndexFunc(s, f) >= 0 135 } 136 137 // Insert inserts the values v... into s at index i, 138 // returning the modified slice. 139 // In the returned slice r, r[i] == v[0]. 140 // Insert panics if i is out of range. 141 // This function is O(len(s) + len(v)). 142 func Insert[S ~[]E, E any](s S, i int, v ...E) S { 143 tot := len(s) + len(v) 144 if tot <= cap(s) { 145 s2 := s[:tot] 146 copy(s2[i+len(v):], s[i:]) 147 copy(s2[i:], v) 148 return s2 149 } 150 s2 := make(S, tot) 151 copy(s2, s[:i]) 152 copy(s2[i:], v) 153 copy(s2[i+len(v):], s[i:]) 154 return s2 155 } 156 157 // Delete removes the elements s[i:j] from s, returning the modified slice. 158 // Delete panics if s[i:j] is not a valid slice of s. 159 // Delete modifies the contents of the slice s; it does not create a new slice. 160 // Delete is O(len(s)-j), so if many items must be deleted, it is better to 161 // make a single call deleting them all together than to delete one at a time. 162 // Delete might not modify the elements s[len(s)-(j-i):len(s)]. If those 163 // elements contain pointers you might consider zeroing those elements so that 164 // objects they reference can be garbage collected. 165 func Delete[S ~[]E, E any](s S, i, j int) S { 166 _ = s[i:j] // bounds check 167 168 return append(s[:i], s[j:]...) 169 } 170 171 // Replace replaces the elements s[i:j] by the given v, and returns the 172 // modified slice. Replace panics if s[i:j] is not a valid slice of s. 173 func Replace[S ~[]E, E any](s S, i, j int, v ...E) S { 174 _ = s[i:j] // verify that i:j is a valid subslice 175 tot := len(s[:i]) + len(v) + len(s[j:]) 176 if tot <= cap(s) { 177 s2 := s[:tot] 178 copy(s2[i+len(v):], s[j:]) 179 copy(s2[i:], v) 180 return s2 181 } 182 s2 := make(S, tot) 183 copy(s2, s[:i]) 184 copy(s2[i:], v) 185 copy(s2[i+len(v):], s[j:]) 186 return s2 187 } 188 189 // Clone returns a copy of the slice. 190 // The elements are copied using assignment, so this is a shallow clone. 191 func Clone[S ~[]E, E any](s S) S { 192 // Preserve nil in case it matters. 193 if s == nil { 194 return nil 195 } 196 return append(S([]E{}), s...) 197 } 198 199 // Compact replaces consecutive runs of equal elements with a single copy. 200 // This is like the uniq command found on Unix. 201 // Compact modifies the contents of the slice s; it does not create a new slice. 202 // When Compact discards m elements in total, it might not modify the elements 203 // s[len(s)-m:len(s)]. If those elements contain pointers you might consider 204 // zeroing those elements so that objects they reference can be garbage collected. 205 func Compact[S ~[]E, E comparable](s S) S { 206 if len(s) < 2 { 207 return s 208 } 209 i := 1 210 for k := 1; k < len(s); k++ { 211 if s[k] != s[k-1] { 212 if i != k { 213 s[i] = s[k] 214 } 215 i++ 216 } 217 } 218 return s[:i] 219 } 220 221 // CompactFunc is like Compact but uses a comparison function. 222 func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { 223 if len(s) < 2 { 224 return s 225 } 226 i := 1 227 for k := 1; k < len(s); k++ { 228 if !eq(s[k], s[k-1]) { 229 if i != k { 230 s[i] = s[k] 231 } 232 i++ 233 } 234 } 235 return s[:i] 236 } 237 238 // Grow increases the slice's capacity, if necessary, to guarantee space for 239 // another n elements. After Grow(n), at least n elements can be appended 240 // to the slice without another allocation. If n is negative or too large to 241 // allocate the memory, Grow panics. 242 func Grow[S ~[]E, E any](s S, n int) S { 243 if n < 0 { 244 panic("cannot be negative") 245 } 246 if n -= cap(s) - len(s); n > 0 { 247 // TODO(https://go.dev/issue/53888): Make using []E instead of S 248 // to workaround a compiler bug where the runtime.growslice optimization 249 // does not take effect. Revert when the compiler is fixed. 250 s = append([]E(s)[:cap(s)], make([]E, n)...)[:len(s)] 251 } 252 return s 253 } 254 255 // Clip removes unused capacity from the slice, returning s[:len(s):len(s)]. 256 func Clip[S ~[]E, E any](s S) S { 257 return s[:len(s):len(s)] 258 }