github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/easy/slices.go (about) 1 package easy 2 3 import ( 4 "sort" 5 6 "github.com/jxskiss/gopkg/v2/internal/constraints" 7 ) 8 9 // Clip removes unused capacity from the slice, returning s[:len(s):len(s)]. 10 func Clip[S ~[]E, E any](s S) S { 11 return s[:len(s):len(s)] 12 } 13 14 // Copy copies a slice to be a new one. 15 // optionalCap optionally specifies the capacity of the new slice. 16 func Copy[S ~[]E, E any](s S, optionalCap ...int) S { 17 copyCap := len(s) 18 if len(optionalCap) > 0 && optionalCap[0] > copyCap { 19 copyCap = optionalCap[0] 20 } 21 out := make(S, len(s), copyCap) 22 copy(out, s) 23 return out 24 } 25 26 // Concat concatenates given slices into a single slice. 27 func Concat[S ~[]E, E any](slices ...S) S { 28 n := 0 29 for _, s := range slices { 30 n += len(s) 31 } 32 out := make(S, 0, n) 33 for _, s := range slices { 34 out = append(out, s...) 35 } 36 return out 37 } 38 39 // Count iterates slices, it calls predicate(elem) for 40 // each elem in the slices and returns the count of elements for which 41 // predicate(elem) returns true. 42 func Count[S ~[]E, E any](predicate func(elem E) bool, slices ...S) int { 43 count := 0 44 for _, s := range slices { 45 for _, e := range s { 46 if predicate(e) { 47 count++ 48 } 49 } 50 } 51 return count 52 } 53 54 // Diff allocates and returns a new slice which contains the values 55 // which present in slice, but not present in others. 56 // 57 // If length of slice is zero, it returns nil. 58 func Diff[S ~[]E, E comparable](slice S, others ...S) S { 59 return diffSlice(false, slice, others...) 60 } 61 62 // DiffInplace returns a slice which contains the values which present 63 // in slice, but not present in others. 64 // It does not allocate new memory, but modifies slice in-place. 65 // 66 // If length of slice is zero, it returns nil. 67 func DiffInplace[S ~[]E, E comparable](slice S, others ...S) S { 68 return diffSlice(true, slice, others...) 69 } 70 71 func diffSlice[S ~[]E, E comparable](inplace bool, slice S, others ...S) S { 72 if len(slice) == 0 { 73 return nil 74 } 75 s2set := make(map[E]struct{}) 76 for _, s := range others { 77 for _, x := range s { 78 s2set[x] = struct{}{} 79 } 80 } 81 out := slice[:0] 82 if !inplace { 83 out = make(S, 0, len(slice)) 84 } 85 for _, x := range slice { 86 if _, ok := s2set[x]; !ok { 87 out = append(out, x) 88 } 89 } 90 return out 91 } 92 93 // Filter iterates the given slices, it calls predicate(i, elem) for 94 // each elem in the slices and returns a new slice of elements for which 95 // predicate(i, elem) returns true. 96 func Filter[S ~[]E, E any](predicate func(i int, elem E) bool, slices ...S) S { 97 if len(slices) == 0 { 98 return nil 99 } 100 out := make(S, 0, len(slices[0])) 101 for _, s := range slices { 102 for i, e := range s { 103 if predicate(i, e) { 104 out = append(out, e) 105 } 106 } 107 } 108 return out 109 } 110 111 // FilterInMap returns a slice containing all elements in s that is also in m. 112 // When inplace is true, it writes result to the given slice s, 113 // else it allocates a new slice. 114 func FilterInMap[S ~[]E, M ~map[E]V, E comparable, V any](s S, m M, inplace bool) S { 115 var out S 116 if inplace { 117 out = s[:0] 118 } 119 for _, x := range s { 120 if _, ok := m[x]; ok { 121 out = append(out, x) 122 } 123 } 124 return out 125 } 126 127 // FilterNotInMap returns a slice containing all elements in s but not in m. 128 // When inplace is true, it writes result to the given slice s, 129 // else it allocates a new slice. 130 func FilterNotInMap[S ~[]E, M ~map[E]V, E comparable, V any](s S, m M, inplace bool) S { 131 var out S 132 if inplace { 133 out = s[:0] 134 } 135 for _, x := range s { 136 if _, ok := m[x]; !ok { 137 out = append(out, x) 138 } 139 } 140 return out 141 } 142 143 // InSlice tells whether the value elem is in the slice. 144 func InSlice[E comparable](slice []E, elem E) bool { 145 return Index(slice, elem) >= 0 146 } 147 148 // Index returns the index of the first occurrence of v in s, 149 // or -1 if not present. 150 func Index[S ~[]E, E comparable](s S, v E) int { 151 for i, vs := range s { 152 if v == vs { 153 return i 154 } 155 } 156 return -1 157 } 158 159 // IndexFunc iterates the given slice, it calls predicate(i) for i in 160 // range [0, n) where n is the length of the slice. 161 // When predicate(i) returns true, it stops and returns the index i. 162 func IndexFunc[E any](slice []E, predicate func(i int) bool) int { 163 for i := range slice { 164 if predicate(i) { 165 return i 166 } 167 } 168 return -1 169 } 170 171 // LastIndex returns the index of the last instance of v in s, 172 // or -1 if v is not present in s. 173 func LastIndex[S ~[]E, E comparable](s S, v E) int { 174 for i := len(s) - 1; i >= 0; i-- { 175 if s[i] == v { 176 return i 177 } 178 } 179 return -1 180 } 181 182 // LastIndexFunc iterates the given slice, it calls predicate(i) for i in 183 // range [0, n) in descending order, where n is the length of the slice. 184 // When predicate(i) returns true, it stops and returns the index i. 185 func LastIndexFunc[E any](slice []E, predicate func(i int) bool) int { 186 for i := len(slice) - 1; i >= 0; i-- { 187 if predicate(i) { 188 return i 189 } 190 } 191 return -1 192 } 193 194 // IJ represents a batch index of i, j. 195 type IJ struct{ I, J int } 196 197 // SplitBatch splits a large number to batch, it's mainly designed to 198 // help operations with large slice, such as inserting lots of records 199 // into database, or logging lots of identifiers, etc. 200 func SplitBatch(total, batch int) []IJ { 201 if total <= 0 { 202 return nil 203 } 204 if batch <= 0 { 205 return []IJ{{0, total}} 206 } 207 n := total/batch + 1 208 ret := make([]IJ, n) 209 idx := 0 210 for i, j := 0, batch; idx < n && i < total; i, j = i+batch, j+batch { 211 if j > total { 212 j = total 213 } 214 ret[idx] = IJ{i, j} 215 idx++ 216 } 217 return ret[:idx] 218 } 219 220 // Split splits a large slice []T to batches, it returns a slice 221 // of type [][]T whose elements are sub slices of slice. 222 func Split[S ~[]E, E any](slice S, batch int) []S { 223 if len(slice) == 0 { 224 return nil 225 } 226 if batch <= 0 { 227 return []S{slice} 228 } 229 n := len(slice) / batch 230 ret := make([]S, 0, n+1) 231 for i := 0; i < n*batch; i += batch { 232 ret = append(ret, slice[i:i+batch]) 233 } 234 if last := n * batch; last < len(slice) { 235 ret = append(ret, slice[last:]) 236 } 237 return ret 238 } 239 240 // Repeat returns a new slice consisting of count copies of the slice s. 241 // 242 // It panics if count is zero or negative or if 243 // the result of (len(s) * count) overflows. 244 func Repeat[S ~[]E, E any](s S, count int) S { 245 if count <= 0 { 246 panic("zero or negative Repeat count") 247 } else if len(s)*count/count != len(s) { 248 panic("Repeat count causes overflow") 249 } 250 251 out := make(S, 0, count*len(s)) 252 for i := 0; i < count; i++ { 253 out = append(out, s...) 254 } 255 return out 256 } 257 258 // Reverse returns a slice of the elements in reversed order. 259 // When inplace is true, it does not allocate new memory, but the slice 260 // is reversed in place. 261 func Reverse[S ~[]E, E any](s S, inplace bool) S { 262 if s == nil { 263 return nil 264 } 265 out := s 266 if !inplace { 267 out = make(S, len(s)) 268 copy(out, s) 269 } 270 i, j := 0, len(s)-1 271 for i < j { 272 out[i], out[j] = out[j], out[i] 273 i++ 274 j-- 275 } 276 return out 277 } 278 279 // Unique returns a slice containing the elements of the given 280 // slice in same order, but removes duplicate values. 281 // When inplace is true, it does not allocate new memory, the unique values 282 // will be written to the input slice from the beginning. 283 // 284 // Given different input, the duplication rate may be varying, 285 // for large input, this function does not assume any specific workload type, 286 // it allocates initial memory of size `len(s)/2`, thus for slice that 287 // most elements are duplicate, it allocates memory more than need, 288 // but for slice that no value is duplicate, it triggers memory allocation 289 // more than once. 290 // For large slice in performance critical use-case, user is recommended to 291 // write a custom function that is fine-tuned for specific workload to get 292 // the best performance. 293 func Unique[S ~[]E, E comparable](s S, inplace bool) S { 294 if s == nil { 295 return nil 296 } 297 var out S 298 if inplace { 299 out = s[:0] 300 } 301 302 // According to benchmark results, 128 is a reasonable choice 303 // to balance the performance of different algorithms and the cost 304 // of memory allocation. 305 // See BenchmarkUnique* in slices_test.go. 306 if len(s) <= 128 { 307 return uniqueByLoopCmp(out, s) 308 } 309 return uniqueByHashset(out, s) 310 } 311 312 func uniqueByLoopCmp[S ~[]E, E comparable](dst, src S) S { 313 if cap(dst) == 0 { 314 dst = make(S, 0, len(src)) 315 } 316 for _, x := range src { 317 isDup := false 318 for i := range dst { 319 if x == dst[i] { 320 isDup = true 321 break 322 } 323 } 324 if !isDup { 325 dst = append(dst, x) 326 } 327 } 328 return dst 329 } 330 331 func uniqueByHashset[S ~[]E, E comparable](dst, src S) S { 332 if cap(dst) == 0 { 333 dst = make(S, 0, len(src)/2) 334 } 335 seen := make(map[E]struct{}, len(src)/2) 336 for _, x := range src { 337 if _, ok := seen[x]; !ok { 338 seen[x] = struct{}{} 339 dst = append(dst, x) 340 } 341 } 342 return dst 343 } 344 345 // UniqueFunc returns a slice containing the elements of the given slice 346 // in same order, but removes deduplicate values, it calls f for each 347 // element and uses the returned value to check duplication. 348 // When inplace is true, it does not allocate new memory, the unique values 349 // will be written to the input slice from the beginning. 350 // 351 // Given different input, the duplication rate may be varying, 352 // for large input, this function does not assume any specific workload type, 353 // it allocates initial memory of size `len(s)/2`, thus for slice that 354 // most elements are duplicate, it allocates memory more than need, 355 // but for slice that no value is duplicate, it triggers memory allocation 356 // more than once. 357 // For large slice in performance critical use-case, user is recommended to 358 // write a custom function that is fine-tuned for specific workload to get 359 // the best performance. 360 func UniqueFunc[S ~[]E, E any, C comparable](s S, inplace bool, f func(E) C) S { 361 if s == nil { 362 return nil 363 } 364 var out S 365 if inplace { 366 out = s[:0] 367 } 368 if len(s) <= 128 { 369 return uniqueFuncByLoopCmp(out, s, f) 370 } 371 return uniqueFuncByHashset(out, s, f) 372 } 373 374 func uniqueFuncByLoopCmp[S ~[]E, E any, C comparable](dst, src S, f func(E) C) S { 375 if cap(dst) == 0 { 376 dst = make(S, 0, len(src)) 377 } 378 seen := make([]C, 0, len(src)) 379 for _, x := range src { 380 c := f(x) 381 isDup := false 382 for i := range seen { 383 if c == seen[i] { 384 isDup = true 385 break 386 } 387 } 388 if !isDup { 389 seen = append(seen, c) 390 dst = append(dst, x) 391 } 392 } 393 return dst 394 } 395 396 func uniqueFuncByHashset[S ~[]E, E any, C comparable](dst, src S, f func(E) C) S { 397 if cap(dst) == 0 { 398 dst = make(S, 0, len(src)/2) 399 } 400 seen := make(map[C]struct{}, len(src)/2) 401 for _, x := range src { 402 c := f(x) 403 if _, ok := seen[c]; !ok { 404 seen[c] = struct{}{} 405 dst = append(dst, x) 406 } 407 } 408 return dst 409 } 410 411 // Sum returns the sum value of the elements in the given slice. 412 func Sum[T constraints.Integer](slice []T) int64 { 413 var sum int64 414 for _, x := range slice { 415 sum += int64(x) 416 } 417 return sum 418 } 419 420 // SumFloat returns the sum value of the elements in the given slice, 421 // as a float64 value. 422 func SumFloat[T constraints.RealNumber](slice []T) float64 { 423 var sum float64 424 for _, x := range slice { 425 sum += float64(x) 426 } 427 return sum 428 } 429 430 // Sort sorts the given slice ascending and returns it. 431 func Sort[S ~[]E, E constraints.Ordered](s S) S { 432 sort.Slice(s, func(i, j int) bool { 433 return s[i] < s[j] 434 }) 435 return s 436 } 437 438 // SortDesc sorts the given slice descending and returns it. 439 func SortDesc[S ~[]E, E constraints.Ordered](s S) S { 440 sort.Slice(s, func(i, j int) bool { 441 return s[j] < s[i] 442 }) 443 return s 444 }