mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 21:02:27 +08:00
733 lines
16 KiB
Go
733 lines
16 KiB
Go
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
|
// Use of this source code is governed by MIT license
|
|
|
|
// Package slice implements some functions to manipulate slice.
|
|
package slice
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"math/rand"
|
|
"reflect"
|
|
"sort"
|
|
)
|
|
|
|
// Contain check if the value is in the slice or not
|
|
func Contain[T any](slice []T, value T) bool {
|
|
for _, v := range slice {
|
|
if reflect.DeepEqual(v, value) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// ContainSubSlice check if the slice contain subslice or not
|
|
func ContainSubSlice[T any](slice, subslice []T) bool {
|
|
for _, v := range subslice {
|
|
if !Contain(slice, v) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
|
|
// unique := make(map[T]bool)
|
|
// for _, v := range slice {
|
|
// unique[v] = true
|
|
// }
|
|
// for _, v := range subslice {
|
|
// if !unique[v] {
|
|
// return false
|
|
// }
|
|
// }
|
|
// return true
|
|
}
|
|
|
|
// Chunk creates an slice of elements split into groups the length of size.
|
|
func Chunk[T any](slice []T, size int) [][]T {
|
|
var res [][]T
|
|
|
|
if len(slice) == 0 || size <= 0 {
|
|
return res
|
|
}
|
|
|
|
length := len(slice)
|
|
if size == 1 || size >= length {
|
|
for _, v := range slice {
|
|
var tmp []T
|
|
tmp = append(tmp, v)
|
|
res = append(res, tmp)
|
|
}
|
|
return res
|
|
}
|
|
|
|
// divide slice equally
|
|
divideNum := length/size + 1
|
|
for i := 0; i < divideNum; i++ {
|
|
if i == divideNum-1 {
|
|
if len(slice[i*size:]) > 0 {
|
|
res = append(res, slice[i*size:])
|
|
}
|
|
} else {
|
|
res = append(res, slice[i*size:(i+1)*size])
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey
|
|
func Compact[T any](slice []T) []T {
|
|
res := make([]T, 0, 0)
|
|
for _, v := range slice {
|
|
if !reflect.DeepEqual(v, nil) &&
|
|
!reflect.DeepEqual(v, false) &&
|
|
!reflect.DeepEqual(v, "") &&
|
|
!reflect.DeepEqual(v, 0) {
|
|
res = append(res, v)
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// Concat creates a new slice concatenating slice with any additional slices and/or values.
|
|
func Concat[T any](slice []T, values ...[]T) []T {
|
|
res := append([]T{}, slice...)
|
|
|
|
for _, v := range values {
|
|
res = append(res, v...)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// Difference creates an slice of whose element in slice but not in comparedSlice
|
|
func Difference[T comparable](slice, comparedSlice []T) []T {
|
|
var res []T
|
|
for _, v := range slice {
|
|
if !Contain(comparedSlice, v) {
|
|
res = append(res, v)
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// DifferenceBy it accepts iteratee which is invoked for each element of slice
|
|
// and values to generate the criterion by which they're compared.
|
|
// like lodash.js differenceBy: https://lodash.com/docs/4.17.15#differenceBy,
|
|
func DifferenceBy[T any](slice []T, comparedSlice []T, iteratee func(index int, t T) T) []T {
|
|
orginSliceAfterMap := Map(slice, iteratee)
|
|
comparedSliceAfterMap := Map(comparedSlice, iteratee)
|
|
|
|
res := make([]T, 0, 0)
|
|
for i, v := range orginSliceAfterMap {
|
|
if !Contain(comparedSliceAfterMap, v) {
|
|
res = append(res, slice[i])
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
//DifferenceWith accepts comparator which is invoked to compare elements of slice to values. The order and references of result values are determined by the first slice. The comparator is invoked with two arguments: (arrVal, othVal).
|
|
func DifferenceWith[T any](slice []T, comparedSlice []T, comparator func(value, otherValue T) bool) []T {
|
|
res := make([]T, 0, 0)
|
|
|
|
getIndex := func(arr []T, item T, comparison func(v1, v2 T) bool) int {
|
|
index := -1
|
|
for i, v := range arr {
|
|
if comparison(item, v) {
|
|
index = i
|
|
break
|
|
}
|
|
}
|
|
return index
|
|
}
|
|
|
|
for i, v := range slice {
|
|
index := getIndex(comparedSlice, v, comparator)
|
|
if index == -1 {
|
|
res = append(res, slice[i])
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// Every return true if all of the values in the slice pass the predicate function.
|
|
func Every[T any](slice []T, predicate func(index int, t T) bool) bool {
|
|
if predicate == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
var currentLength int
|
|
for i, v := range slice {
|
|
if predicate(i, v) {
|
|
currentLength++
|
|
}
|
|
}
|
|
|
|
return currentLength == len(slice)
|
|
}
|
|
|
|
// None return true if all the values in the slice mismatch the criteria
|
|
func None[T any](slice []T, predicate func(index int, t T) bool) bool {
|
|
if predicate == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
var currentLength int
|
|
for i, v := range slice {
|
|
if !predicate(i, v) {
|
|
currentLength++
|
|
}
|
|
}
|
|
|
|
return currentLength == len(slice)
|
|
}
|
|
|
|
// Some return true if any of the values in the list pass the predicate function.
|
|
func Some[T any](slice []T, predicate func(index int, t T) bool) bool {
|
|
if predicate == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
for i, v := range slice {
|
|
if predicate(i, v) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Filter iterates over elements of slice, returning an slice of all elements pass the predicate function
|
|
func Filter[T any](slice []T, predicate func(index int, t T) bool) []T {
|
|
if predicate == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
res := make([]T, 0, 0)
|
|
for i, v := range slice {
|
|
if predicate(i, v) {
|
|
res = append(res, v)
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
// Count iterates over elements of slice, returns a count of all matched elements
|
|
func Count[T any](slice []T, predicate func(index int, t T) bool) int {
|
|
if predicate == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
if len(slice) == 0 {
|
|
return 0
|
|
}
|
|
|
|
var count int
|
|
for i, v := range slice {
|
|
if predicate(i, v) {
|
|
count++
|
|
}
|
|
}
|
|
|
|
return count
|
|
}
|
|
|
|
// GroupBy iterate over elements of the slice, each element will be group by criteria, returns two slices
|
|
func GroupBy[T any](slice []T, groupFn func(index int, t T) bool) ([]T, []T) {
|
|
if groupFn == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
if len(slice) == 0 {
|
|
return make([]T, 0), make([]T, 0)
|
|
}
|
|
|
|
groupB := make([]T, 0)
|
|
groupA := make([]T, 0)
|
|
|
|
for i, v := range slice {
|
|
ok := groupFn(i, v)
|
|
if ok {
|
|
groupA = append(groupA, v)
|
|
} else {
|
|
groupB = append(groupB, v)
|
|
}
|
|
}
|
|
|
|
return groupA, groupB
|
|
}
|
|
|
|
// Find iterates over elements of slice, returning the first one that passes a truth test on predicate function.
|
|
// If return T is nil then no items matched the predicate func
|
|
func Find[T any](slice []T, predicate func(index int, t T) bool) (*T, bool) {
|
|
if predicate == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
if len(slice) == 0 {
|
|
return nil, false
|
|
}
|
|
|
|
index := -1
|
|
for i, v := range slice {
|
|
if predicate(i, v) {
|
|
index = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if index == -1 {
|
|
return nil, false
|
|
}
|
|
|
|
return &slice[index], true
|
|
}
|
|
|
|
// FindLast iterates over elements of slice from end to begin, returning the first one that passes a truth test on predicate function.
|
|
// If return T is nil then no items matched the predicate func
|
|
func FindLast[T any](slice []T, predicate func(index int, t T) bool) (*T, bool) {
|
|
if predicate == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
if len(slice) == 0 {
|
|
return nil, false
|
|
}
|
|
|
|
index := -1
|
|
for i := len(slice) - 1; i >= 0; i-- {
|
|
if predicate(i, slice[i]) {
|
|
index = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if index == -1 {
|
|
return nil, false
|
|
}
|
|
|
|
return &slice[index], true
|
|
}
|
|
|
|
// FindLast iterates over elements of slice from end to begin, returning the first one that passes a truth test on function.
|
|
// The function signature should be func(index int, value interface{}) bool .
|
|
func FindLast(slice, function interface{}) (interface{}, bool) {
|
|
sv := sliceValue(slice)
|
|
fn := functionValue(function)
|
|
|
|
elemType := sv.Type().Elem()
|
|
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
|
panic("function param should be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
|
}
|
|
|
|
index := -1
|
|
for i := sv.Len() - 1; i >= 0; i-- {
|
|
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
|
|
if flag.Bool() {
|
|
index = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if index == -1 {
|
|
var none interface{}
|
|
return none, false
|
|
}
|
|
|
|
return sv.Index(index).Interface(), true
|
|
}
|
|
|
|
// FlattenDeep flattens slice recursive
|
|
func FlattenDeep(slice interface{}) interface{} {
|
|
sv := sliceValue(slice)
|
|
st := sliceElemType(sv.Type())
|
|
tmp := reflect.MakeSlice(reflect.SliceOf(st), 0, 0)
|
|
res := flattenRecursive(sv, tmp)
|
|
return res.Interface()
|
|
}
|
|
|
|
func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value {
|
|
for i := 0; i < value.Len(); i++ {
|
|
item := value.Index(i)
|
|
kind := item.Kind()
|
|
|
|
if kind == reflect.Slice {
|
|
result = flattenRecursive(item, result)
|
|
} else {
|
|
result = reflect.Append(result, item)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// ForEach iterates over elements of slice and invokes function for each element
|
|
func ForEach[T any](slice []T, iteratee func(index int, t T)) {
|
|
if iteratee == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
for i, v := range slice {
|
|
iteratee(i, v)
|
|
}
|
|
}
|
|
|
|
// Map creates an slice of values by running each element of slice thru iteratee function.
|
|
func Map[T any, U any](slice []T, iteratee func(index int, t T) U) []U {
|
|
if iteratee == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
res := make([]U, len(slice), cap(slice))
|
|
for i, v := range slice {
|
|
res[i] = iteratee(i, v)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// Reduce creates an slice of values by running each element of slice thru iteratee function.
|
|
func Reduce[T any](slice []T, iteratee func(index int, t1, t2 T) T, initial T) T {
|
|
if iteratee == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
if len(slice) == 0 {
|
|
return initial
|
|
}
|
|
|
|
res := iteratee(0, initial, slice[0])
|
|
|
|
tmp := make([]T, 2, 2)
|
|
for i := 1; i < len(slice); i++ {
|
|
tmp[0] = res
|
|
tmp[1] = slice[i]
|
|
res = iteratee(i, tmp[0], tmp[1])
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// InterfaceSlice convert param to slice of interface.
|
|
func InterfaceSlice(slice interface{}) []interface{} {
|
|
sv := sliceValue(slice)
|
|
if sv.IsNil() {
|
|
return nil
|
|
}
|
|
|
|
res := make([]interface{}, sv.Len())
|
|
for i := 0; i < sv.Len(); i++ {
|
|
res[i] = sv.Index(i).Interface()
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// StringSlice convert param to slice of string.
|
|
func StringSlice(slice interface{}) []string {
|
|
v := sliceValue(slice)
|
|
|
|
out := make([]string, v.Len())
|
|
for i := 0; i < v.Len(); i++ {
|
|
v, ok := v.Index(i).Interface().(string)
|
|
if !ok {
|
|
panic("invalid element type")
|
|
}
|
|
out[i] = v
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
// IntSlice convert param to slice of int.
|
|
func IntSlice(slice interface{}) []int {
|
|
sv := sliceValue(slice)
|
|
|
|
out := make([]int, sv.Len())
|
|
for i := 0; i < sv.Len(); i++ {
|
|
v, ok := sv.Index(i).Interface().(int)
|
|
if !ok {
|
|
panic("invalid element type")
|
|
}
|
|
out[i] = v
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
// DeleteByIndex delete the element of slice from start index to end index - 1.
|
|
func DeleteByIndex[T any](slice []T, start int, end ...int) []T {
|
|
size := len(slice)
|
|
|
|
if start < 0 || start > size-1 {
|
|
return slice
|
|
}
|
|
|
|
if len(end) > 0 {
|
|
end := end[0]
|
|
if end <= start {
|
|
return slice
|
|
}
|
|
if end > size {
|
|
end = size
|
|
}
|
|
|
|
slice = append(slice[:start], slice[end:]...)
|
|
return slice
|
|
}
|
|
|
|
if start == size-1 {
|
|
slice = append(slice[:start])
|
|
} else {
|
|
slice = append(slice[:start], slice[start+1:]...)
|
|
}
|
|
|
|
return slice
|
|
}
|
|
|
|
// Drop creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0
|
|
func Drop[T any](slice []T, n int) []T {
|
|
size := len(slice)
|
|
|
|
if size == 0 || n == 0 {
|
|
return slice
|
|
}
|
|
|
|
if math.Abs(float64(n)) >= float64(size) {
|
|
return []T{}
|
|
}
|
|
|
|
if n < 0 {
|
|
return slice[0 : size+n]
|
|
}
|
|
|
|
return slice[n:size]
|
|
}
|
|
|
|
// InsertByIndex insert the value or other slice into slice at index.
|
|
func InsertByIndex[T any](slice []T, index int, value interface{}) []T {
|
|
size := len(slice)
|
|
|
|
if index < 0 || index > size {
|
|
return slice
|
|
}
|
|
|
|
// value is T
|
|
if v, ok := value.(T); ok {
|
|
slice = append(slice[:index], append([]T{v}, slice[index:]...)...)
|
|
return slice
|
|
}
|
|
|
|
// value is []T
|
|
if v, ok := value.([]T); ok {
|
|
slice = append(slice[:index], append(v, slice[index:]...)...)
|
|
return slice
|
|
}
|
|
|
|
return slice
|
|
}
|
|
|
|
// UpdateByIndex update the slice element at index.
|
|
func UpdateByIndex[T any](slice []T, index int, value T) []T {
|
|
size := len(slice)
|
|
|
|
if index < 0 || index >= size {
|
|
return slice
|
|
}
|
|
slice = append(slice[:index], append([]T{value}, slice[index+1:]...)...)
|
|
|
|
return slice
|
|
}
|
|
|
|
// Unique remove duplicate elements in slice.
|
|
func Unique[T any](slice []T) []T {
|
|
if len(slice) == 0 {
|
|
return []T{}
|
|
}
|
|
|
|
// here no use map filter. if use it, the result slice element order is random, not same as origin slice
|
|
var res []T
|
|
for i := 0; i < len(slice); i++ {
|
|
v := slice[i]
|
|
skip := true
|
|
for j := range res {
|
|
if reflect.DeepEqual(v, res[j]) {
|
|
skip = false
|
|
break
|
|
}
|
|
}
|
|
if skip {
|
|
res = append(res, v)
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons.
|
|
func Union[T any](slices ...[]T) []T {
|
|
if len(slices) == 0 {
|
|
return []T{}
|
|
}
|
|
|
|
// append all slices, then unique it
|
|
var allElements []T
|
|
|
|
for _, slice := range slices {
|
|
for _, v := range slice {
|
|
allElements = append(allElements, v)
|
|
}
|
|
}
|
|
|
|
return Unique(allElements)
|
|
}
|
|
|
|
// Intersection creates a slice of unique values that included by all slices.
|
|
func Intersection[T any](slices ...[]T) []T {
|
|
var res []T
|
|
if len(slices) == 0 {
|
|
return []T{}
|
|
}
|
|
if len(slices) == 1 {
|
|
return Unique(slices[0])
|
|
}
|
|
|
|
//return elements both in slice1 and slice2
|
|
reduceFunc := func(slice1, slice2 []T) []T {
|
|
s := make([]T, 0, 0)
|
|
for _, v := range slice1 {
|
|
if Contain(slice2, v) {
|
|
s = append(s, v)
|
|
}
|
|
}
|
|
return s
|
|
}
|
|
|
|
res = reduceFunc(slices[0], slices[1])
|
|
|
|
if len(slices) == 2 {
|
|
return Unique(res)
|
|
}
|
|
|
|
tmp := make([][]T, 2, 2)
|
|
for i := 2; i < len(slices); i++ {
|
|
tmp[0] = res
|
|
tmp[1] = slices[i]
|
|
res = reduceFunc(tmp[0], tmp[1])
|
|
}
|
|
|
|
return Unique(res)
|
|
}
|
|
|
|
// Reverse return slice of element order is reversed to the given slice
|
|
func Reverse[T any](slice []T) {
|
|
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
|
|
slice[i], slice[j] = slice[j], slice[i]
|
|
}
|
|
}
|
|
|
|
// Shuffle creates an slice of shuffled values
|
|
func Shuffle[T any](slice []T) []T {
|
|
|
|
res := make([]T, len(slice))
|
|
for i, v := range rand.Perm(len(slice)) {
|
|
res[i] = slice[v]
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// SortByField return sorted slice by field
|
|
// Slice element should be struct, field type should be int, uint, string, or bool
|
|
// default sortType is ascending (asc), if descending order, set sortType to desc
|
|
func SortByField(slice interface{}, field string, sortType ...string) error {
|
|
sv := sliceValue(slice)
|
|
t := sv.Type().Elem()
|
|
|
|
if t.Kind() == reflect.Ptr {
|
|
t = t.Elem()
|
|
}
|
|
if t.Kind() != reflect.Struct {
|
|
return fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", slice)
|
|
}
|
|
|
|
// Find the field.
|
|
sf, ok := t.FieldByName(field)
|
|
if !ok {
|
|
return fmt.Errorf("field name %s not found", field)
|
|
}
|
|
|
|
// Create a less function based on the field's kind.
|
|
var compare func(a, b reflect.Value) bool
|
|
switch sf.Type.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
if len(sortType) > 0 && sortType[0] == "desc" {
|
|
compare = func(a, b reflect.Value) bool { return a.Int() > b.Int() }
|
|
} else {
|
|
compare = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
|
}
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
if len(sortType) > 0 && sortType[0] == "desc" {
|
|
compare = func(a, b reflect.Value) bool { return a.Uint() > b.Uint() }
|
|
} else {
|
|
compare = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
|
}
|
|
case reflect.Float32, reflect.Float64:
|
|
if len(sortType) > 0 && sortType[0] == "desc" {
|
|
compare = func(a, b reflect.Value) bool { return a.Float() > b.Float() }
|
|
} else {
|
|
compare = func(a, b reflect.Value) bool { return a.Float() < b.Float() }
|
|
}
|
|
case reflect.String:
|
|
if len(sortType) > 0 && sortType[0] == "desc" {
|
|
compare = func(a, b reflect.Value) bool { return a.String() > b.String() }
|
|
} else {
|
|
compare = func(a, b reflect.Value) bool { return a.String() < b.String() }
|
|
}
|
|
case reflect.Bool:
|
|
if len(sortType) > 0 && sortType[0] == "desc" {
|
|
compare = func(a, b reflect.Value) bool { return a.Bool() && !b.Bool() }
|
|
} else {
|
|
compare = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() }
|
|
}
|
|
default:
|
|
return fmt.Errorf("field type %s not supported", sf.Type)
|
|
}
|
|
|
|
sort.Slice(slice, func(i, j int) bool {
|
|
a := sv.Index(i)
|
|
b := sv.Index(j)
|
|
if t.Kind() == reflect.Ptr {
|
|
a = a.Elem()
|
|
b = b.Elem()
|
|
}
|
|
a = a.FieldByIndex(sf.Index)
|
|
b = b.FieldByIndex(sf.Index)
|
|
return compare(a, b)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// Without creates a slice excluding all given values
|
|
func Without[T any](slice []T, values ...T) []T {
|
|
if len(values) == 0 || len(slice) == 0 {
|
|
return slice
|
|
}
|
|
|
|
out := make([]T, 0, len(slice))
|
|
for _, v := range slice {
|
|
if !Contain(values, v) {
|
|
out = append(out, v)
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|