mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 12:52:28 +08:00
637 lines
14 KiB
Go
637 lines
14 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"
|
|
"unsafe"
|
|
)
|
|
|
|
// Contain check if the value is in the iterable type or not
|
|
func Contain[T comparable](slice []T, value T) bool {
|
|
for _, v := range slice {
|
|
if v == value {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// ContainSubSlice check if the slice contain subslice or not
|
|
func ContainSubSlice[T comparable](slice, subslice []T) bool {
|
|
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
|
|
}
|
|
|
|
// Difference creates an slice of whose element in slice1 but not in slice2
|
|
func Difference[T comparable](slice1, slice2 []T) []T {
|
|
var res []T
|
|
for _, v := range slice1 {
|
|
if !Contain(slice2, v) {
|
|
res = append(res, v)
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// Every return true if all of the values in the slice pass the predicate function.
|
|
// The fn function signature should be func(int, T) bool .
|
|
func Every[T any](slice []T, fn func(index int, t T) bool) bool {
|
|
if fn == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
var currentLength int
|
|
for i, v := range slice {
|
|
if fn(i, v) {
|
|
currentLength++
|
|
}
|
|
}
|
|
|
|
return currentLength == len(slice)
|
|
}
|
|
|
|
// None return true if all the values in the slice mismatch the criteria
|
|
// The fn function signature should be func(int, T) bool .
|
|
func None[T any](slice []T, fn func(index int, t T) bool) bool {
|
|
if fn == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
var currentLength int
|
|
for i, v := range slice {
|
|
if !fn(i, v) {
|
|
currentLength++
|
|
}
|
|
}
|
|
|
|
return currentLength == len(slice)
|
|
}
|
|
|
|
// Some return true if any of the values in the list pass the predicate function.
|
|
// The fn function signature should be func(int, T) bool.
|
|
func Some[T any](slice []T, fn func(index int, t T) bool) bool {
|
|
if fn == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
for i, v := range slice {
|
|
if fn(i, v) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Filter iterates over elements of slice, returning an slice of all elements `signature` returns truthy for.
|
|
// The fn function signature should be func(int, T) bool.
|
|
func Filter[T any](slice []T, fn func(index int, t T) bool) []T {
|
|
if fn == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
res := make([]T, 0, 0)
|
|
for i, v := range slice {
|
|
if fn(i, v) {
|
|
res = append(res, v)
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
// Count iterates over elements of slice, returns a count of all matched elements
|
|
// The function signature should be func(int, T) bool .
|
|
func Count[T any](slice []T, fn func(index int, t T) bool) int {
|
|
if fn == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
if len(slice) == 0 {
|
|
return 0
|
|
}
|
|
|
|
var count int
|
|
for i, v := range slice {
|
|
if fn(i, v) {
|
|
count++
|
|
}
|
|
}
|
|
|
|
return count
|
|
}
|
|
|
|
// GroupBy iterate over elements of the slice, each element will be group by criteria, returns two slices
|
|
// The function signature should be func(index int, T) bool .
|
|
func GroupBy[T any](slice []T, fn func(index int, t T) bool) ([]T, []T) {
|
|
|
|
if fn == 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 := fn(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 function.
|
|
// The fn function signature should be func(int, T) bool .
|
|
// If return T is nil then no items matched the predicate func
|
|
func Find[T any](slice []T, fn func(index int, t T) bool) (*T, bool) {
|
|
if fn == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
if len(slice) == 0 {
|
|
return nil, false
|
|
}
|
|
|
|
index := -1
|
|
for i, v := range slice {
|
|
if fn(i, v) {
|
|
index = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if index == -1 {
|
|
return nil, false
|
|
}
|
|
|
|
return &slice[index], 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
|
|
// The fn function signature should be func(int, T).
|
|
func ForEach[T any](slice []T, fn func(index int, t T)) {
|
|
if fn == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
for i, v := range slice {
|
|
fn(i, v)
|
|
}
|
|
}
|
|
|
|
// Map creates an slice of values by running each element of slice thru fn function.
|
|
// The fn function signature should be func(int, T).
|
|
func Map[T any, U any](slice []T, fn func(index int, t T) U) []U {
|
|
if fn == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
res := make([]U, len(slice), cap(slice))
|
|
for i, v := range slice {
|
|
res[i] = fn(i, v)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// Reduce creates an slice of values by running each element of slice thru fn function.
|
|
// The fn function signature should be fn func(int, T) T .
|
|
func Reduce[T any](slice []T, fn func(index int, t1, t2 T) T, initial T) T {
|
|
if fn == nil {
|
|
panic("fn is missing")
|
|
}
|
|
|
|
if len(slice) == 0 {
|
|
return initial
|
|
}
|
|
|
|
res := fn(0, initial, slice[0])
|
|
|
|
tmp := make([]T, 2, 2)
|
|
for i := 1; i < len(slice); i++ {
|
|
tmp[0] = res
|
|
tmp[1] = slice[i]
|
|
res = fn(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
|
|
}
|
|
|
|
// ConvertSlice convert original slice to new data type element of slice.
|
|
func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} {
|
|
sv := sliceValue(originalSlice)
|
|
if newSliceType.Kind() != reflect.Slice {
|
|
panic(fmt.Sprintf("Invalid newSliceType(non-slice type of type %T)", newSliceType))
|
|
}
|
|
|
|
newSlice := reflect.New(newSliceType)
|
|
|
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(newSlice.Pointer()))
|
|
|
|
var newElemSize = int(sv.Type().Elem().Size()) / int(newSliceType.Elem().Size())
|
|
|
|
hdr.Cap = sv.Cap() * newElemSize
|
|
hdr.Len = sv.Len() * newElemSize
|
|
hdr.Data = sv.Pointer()
|
|
|
|
return newSlice.Elem().Interface()
|
|
}
|
|
|
|
// 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 comparable](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 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 comparable](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 comparable](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 comparable](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
|
|
}
|