package slice import ( "fmt" "math" "reflect" ) // Intersection creates a slice of unique values that included by all slices. func Intersection[T any](slices ...[]T) []T { if len(slices) == 0 { return []T{} } if len(slices) == 1 { return Unique(slices[0]) } var res []T reducer := func(s1, s2 []T) []T { s := make([]T, 0, 0) for _, v := range s1 { if Contain(s2, v) { s = append(s, v) } } return s } res = reducer(slices[0], slices[1]) reduceSlice := make([][]T, 2, 2) for i := 2; i < len(slices); i++ { reduceSlice[0] = res reduceSlice[1] = slices[i] res = reducer(reduceSlice[0], reduceSlice[1]) } return Unique(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) } // UniqueBy call iteratee func with every item of slice, then remove duplicated. func UniqueBy[T any](slice []T, iteratee func(item T) T) []T { if len(slice) == 0 { return []T{} } var res []T for _, v := range slice { val := iteratee(v) res = append(res, val) } return Unique(res) } // 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 } // UpdateAt update the slice element at index. func UpdateAt[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 } // InsertAt insert the value or other slice into slice at index. func InsertAt[T any](slice []T, index int, value any) []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 } // 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] } // DeleteAt delete the element of slice from start index to end index - 1. func DeleteAt[T any](slice []T, start int, end ...int) []T { size := len(slice) if start < 0 || start >= size { 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 } // IntSlice convert param to slice of int. func IntSlice(slice any) []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 } // StringSlice convert param to slice of string. func StringSlice(slice any) []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 } // InterfaceSlice convert param to slice of interface. func InterfaceSlice(slice any) []any { sv := sliceValue(slice) if sv.IsNil() { return nil } res := make([]any, sv.Len()) for i := 0; i < sv.Len(); i++ { res[i] = sv.Index(i).Interface() } return res } // None return true if all the values in the slice mismatch the criteria func None[T any](slice []T, predicate func(index int, item T) bool) bool { if predicate == nil { panic("predicate func 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, item T) bool) bool { if predicate == nil { panic("predicate func 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, item T) bool) []T { if predicate == nil { panic("predicate func 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, item T) bool) int { if predicate == nil { panic("predicate func 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, item T) bool) ([]T, []T) { if groupFn == nil { panic("groupFn func 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 } // GroupWith return a map composed of keys generated from the results of running each element of slice thru iteratee. func GroupWith[T any, U comparable](slice []T, iteratee func(T) U) map[U][]T { if iteratee == nil { panic("iteratee func is missing") } res := make(map[U][]T) for _, v := range slice { key := iteratee(v) if _, ok := res[key]; !ok { res[key] = []T{} } res[key] = append(res[key], v) } return res } // 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, item T) bool) (*T, bool) { if predicate == nil { panic("predicate func 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, item T) bool) (*T, bool) { if predicate == nil { panic("predicate func 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 } // FlattenDeep flattens slice recursive func FlattenDeep(slice any) any { 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 } // 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, item1, item2 T) T, initial T) T { if iteratee == nil { panic("iteratee func 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 } // ForEach iterates over elements of slice and invokes function for each element func ForEach[T any](slice []T, iteratee func(index int, item T)) { if iteratee == nil { panic("iteratee func is missing") } for i, v := range slice { iteratee(i, v) } } // 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, item T) bool) bool { if predicate == nil { panic("predicate func is missing") } var currentLength int for i, v := range slice { if predicate(i, v) { currentLength++ } } return currentLength == len(slice) } // EqualWith checks if two slices are equal with comparator func func EqualWith[T, U any](slice1 []T, slice2 []U, comparator func(T, U) bool) bool { if len(slice1) != len(slice2) { return false } for i, v1 := range slice1 { v2 := slice2[i] if !comparator(v1, v2) { return false } } return true } // Equal checks if two slices are equal: the same length and all elements' order and value are equal func Equal[T comparable](slice1, slice2 []T) bool { if len(slice1) != len(slice2) { return false } for i := range slice1 { if slice1[i] != slice2[i] { return false } } return true } // 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 } // 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, item 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 } // 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, item T) U) []U { if iteratee == nil { panic("iteratee func is missing") } res := make([]U, len(slice), cap(slice)) for i, v := range slice { res[i] = iteratee(i, 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 } // 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 } // 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 } // Chunk 按照size参数均分slice 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 } // ContainSubSlice 判断slice是否包含subslice func ContainSubSlice[T any](slice, subslice []T) bool { for _, v := range subslice { if !Contain(slice, v) { return false } } return true } // Contain 判断slice是否包含value func Contain[T any](slice []T, value T) bool { for _, v := range slice { if reflect.DeepEqual(v, value) { return true } } return false } // sliceValue return the reflect value of a slice func sliceValue(slice any) reflect.Value { v := reflect.ValueOf(slice) if v.Kind() != reflect.Slice { panic(fmt.Sprintf("Invalid slice type, value of type %T", slice)) } return v } // functionValue return the reflect value of a function func functionValue(function any) reflect.Value { v := reflect.ValueOf(function) if v.Kind() != reflect.Func { panic(fmt.Sprintf("Invalid function type, value of type %T", function)) } return v } // checkSliceCallbackFuncSignature Check func sign : s :[]type1{} -> func(i int, data type1) type2 // see https://coolshell.cn/articles/21164.html#%E6%B3%9B%E5%9E%8BMap-Reduce func checkSliceCallbackFuncSignature(fn reflect.Value, types ...reflect.Type) bool { //Check it is a function if fn.Kind() != reflect.Func { return false } // NumIn() - returns a function type's input parameter count. // NumOut() - returns a function type's output parameter count. if (fn.Type().NumIn() != len(types)-1) || (fn.Type().NumOut() != 1) { return false } // In() - returns the type of a function type's i'th input parameter. // first input param type should be int if fn.Type().In(0) != reflect.TypeOf(1) { return false } for i := 0; i < len(types)-1; i++ { if fn.Type().In(i) != types[i] { return false } } // Out() - returns the type of a function type's i'th output parameter. outType := types[len(types)-1] if outType != nil && fn.Type().Out(0) != outType { return false } return true } // sliceElemType get slice element type func sliceElemType(reflectType reflect.Type) reflect.Type { for { if reflectType.Kind() != reflect.Slice { return reflectType } reflectType = reflectType.Elem() } }