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  }