mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-08 14:42:27 +08:00
publish lancet
This commit is contained in:
411
slice/slice.go
Normal file
411
slice/slice.go
Normal file
@@ -0,0 +1,411 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Contain check if the value is in the slice or not
|
||||
func Contain(slice interface{}, value interface{}) bool {
|
||||
v := reflect.ValueOf(slice)
|
||||
switch reflect.TypeOf(slice).Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if v.Index(i).Interface() == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
if v.MapIndex(reflect.ValueOf(value)).IsValid() {
|
||||
return true
|
||||
}
|
||||
case reflect.String:
|
||||
s := fmt.Sprintf("%v", slice)
|
||||
ss := fmt.Sprintf("%v", value)
|
||||
return strings.Contains(s, ss)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Chunk creates an slice of elements split into groups the length of `size`.
|
||||
func Chunk(slice []interface{}, size int) [][]interface{} {
|
||||
var res [][]interface{}
|
||||
|
||||
if len(slice) == 0 || size <= 0 {
|
||||
return res
|
||||
}
|
||||
|
||||
length := len(slice)
|
||||
if size == 1 || size >= length {
|
||||
for _, v := range slice {
|
||||
var tmp []interface{}
|
||||
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 not included in the other given slice
|
||||
func Difference(slice1, slice2 interface{}) interface{} {
|
||||
v := sliceValue(slice1)
|
||||
|
||||
var indexes []int
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
vi := v.Index(i).Interface()
|
||||
if !Contain(slice2, vi) {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(v.Type(), len(indexes), len(indexes))
|
||||
for i := range indexes {
|
||||
res.Index(i).Set(v.Index(indexes[i]))
|
||||
}
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// Filter iterates over elements of slice, returning an slice of all elements `signature` returns truthy for.
|
||||
// The function signature should be func(index int, value interface{}) bool .
|
||||
func Filter(slice, function interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(function)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, reflect.ValueOf(true).Type()) {
|
||||
panic("Filter function must be of type func(int, " + elemType.String() + ")" + reflect.ValueOf(true).Type().String())
|
||||
}
|
||||
|
||||
var indexes []int
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
flag := fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0]
|
||||
if flag.Bool() {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(sv.Type(), len(indexes), len(indexes))
|
||||
for i := range indexes {
|
||||
res.Index(i).Set(sv.Index(indexes[i]))
|
||||
}
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// Map creates an slice of values by running each element of `slice` thru `function`.
|
||||
// The function signature should be func(index int, value interface{}) interface{}.
|
||||
func Map(slice, function interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
fn := functionValue(function)
|
||||
|
||||
elemType := sv.Type().Elem()
|
||||
if checkSliceCallbackFuncSignature(fn, elemType, nil) {
|
||||
panic("Map function must be of type func(int, " + elemType.String() + ")" + elemType.String())
|
||||
}
|
||||
|
||||
res := reflect.MakeSlice(sv.Type(), sv.Len(), sv.Len())
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
res.Index(i).Set(fn.Call([]reflect.Value{reflect.ValueOf(i), sv.Index(i)})[0])
|
||||
}
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// Reduce creates an slice of values by running each element of `slice` thru `function`.
|
||||
// The function signature should be func(index int, value1, value2 interface{}) interface{} .
|
||||
func Reduce(slice, function, zero interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
|
||||
len := sv.Len()
|
||||
if len == 0 {
|
||||
return zero
|
||||
} else if len == 1 {
|
||||
return sv.Index(0)
|
||||
}
|
||||
|
||||
elementType := sv.Type().Elem()
|
||||
fn := functionValue(function)
|
||||
|
||||
if checkSliceCallbackFuncSignature(fn, elementType, elementType, elementType) {
|
||||
t := elementType.String()
|
||||
panic("Reduce function must be of type func(int, " + t + ", " + t + ")" + t)
|
||||
}
|
||||
|
||||
var params [3]reflect.Value
|
||||
params[0] = reflect.ValueOf(0)
|
||||
params[1] = sv.Index(0)
|
||||
params[2] = sv.Index(1)
|
||||
|
||||
res := fn.Call(params[:])[0]
|
||||
|
||||
for i := 2; i < len; i++ {
|
||||
params[0] = reflect.ValueOf(i)
|
||||
params[1] = res
|
||||
params[2] = sv.Index(i)
|
||||
res = fn.Call(params[:])[0]
|
||||
}
|
||||
|
||||
return res.Interface()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var res []string
|
||||
|
||||
v := sliceValue(slice)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
res = append(res, fmt.Sprint(v.Index(i)))
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// IntSlice convert param to slice of int.
|
||||
func IntSlice(slice interface{}) ([]int, error) {
|
||||
var res []int
|
||||
|
||||
sv := sliceValue(slice)
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
v := sv.Index(i).Interface()
|
||||
switch v := v.(type) {
|
||||
case int:
|
||||
res = append(res, v)
|
||||
default:
|
||||
return nil, errors.New("InvalidSliceElementType")
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Delete i: s = append(s[:i], s[i+1:]...)
|
||||
// Delete i to j: s = append(s[:i], s[j:]...)
|
||||
func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) {
|
||||
v := sliceValue(slice)
|
||||
i := start
|
||||
if v.Len() == 0 || i < 0 || i > v.Len() {
|
||||
return nil, errors.New("InvalidStartIndex")
|
||||
}
|
||||
if len(end) > 0 {
|
||||
j := end[0]
|
||||
if j <= i || j > v.Len() {
|
||||
return nil, errors.New("InvalidEndIndex")
|
||||
}
|
||||
v = reflect.AppendSlice(v.Slice(0, i), v.Slice(j, v.Len()))
|
||||
} else {
|
||||
v = reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len()))
|
||||
}
|
||||
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// InsertByIndex insert the element into slice at index.
|
||||
// Insert value: s = append(s[:i], append([]T{x}, s[i:]...)...)
|
||||
// Insert slice: a = append(a[:i], append(b, a[i:]...)...)
|
||||
func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) {
|
||||
v := sliceValue(slice)
|
||||
|
||||
if index < 0 || index > v.Len() {
|
||||
return slice, errors.New("InvalidSliceIndex")
|
||||
}
|
||||
|
||||
// value is slice
|
||||
vv := reflect.ValueOf(value)
|
||||
if vv.Kind() == reflect.Slice {
|
||||
if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value).Elem() {
|
||||
return slice, errors.New("InvalidValueType")
|
||||
}
|
||||
v = reflect.AppendSlice(v.Slice(0, index), reflect.AppendSlice(vv.Slice(0, vv.Len()), v.Slice(index, v.Len())))
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// value is not slice
|
||||
if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) {
|
||||
return slice, errors.New("InvalidValueType")
|
||||
}
|
||||
if index == v.Len() {
|
||||
return reflect.Append(v, reflect.ValueOf(value)).Interface(), nil
|
||||
}
|
||||
|
||||
v = reflect.AppendSlice(v.Slice(0, index+1), v.Slice(index, v.Len()))
|
||||
v.Index(index).Set(reflect.ValueOf(value))
|
||||
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// UpdateByIndex update the slice element at index.
|
||||
func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) {
|
||||
v := sliceValue(slice)
|
||||
|
||||
if index < 0 || index >= v.Len() {
|
||||
return slice, errors.New("InvalidSliceIndex")
|
||||
}
|
||||
if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) {
|
||||
return slice, errors.New("InvalidValueType")
|
||||
}
|
||||
|
||||
v.Index(index).Set(reflect.ValueOf(value))
|
||||
|
||||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
// Unique remove duplicate elements in slice.
|
||||
func Unique(slice interface{}) interface{} {
|
||||
sv := sliceValue(slice)
|
||||
if sv.Len() == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
var res []interface{}
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
v := sv.Index(i).Interface()
|
||||
flag := true
|
||||
for j := range res {
|
||||
if v == res[j] {
|
||||
flag = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if flag {
|
||||
res = append(res, v)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
|
||||
// if use map filter, the result slice element order is random, not same as origin slice
|
||||
//mp := make(map[interface{}]bool)
|
||||
//for i := 0; i < sv.Len(); i++ {
|
||||
// v := sv.Index(i).Interface()
|
||||
// mp[v] = true
|
||||
//}
|
||||
//
|
||||
//var res []interface{}
|
||||
//for k := range mp {
|
||||
// res = append(res, mp[k])
|
||||
//}
|
||||
//return res
|
||||
|
||||
}
|
||||
|
||||
// ReverseSlice return slice of element order is reversed to the given slice
|
||||
func ReverseSlice(slice interface{}) {
|
||||
v := sliceValue(slice)
|
||||
swp := reflect.Swapper(v.Interface())
|
||||
for i, j := 0, v.Len()-1; i < j; i, j = i+1, j-1 {
|
||||
swp(i, j)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
v := sliceValue(slice)
|
||||
t := v.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 less func(a, b reflect.Value) bool
|
||||
switch sf.Type.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
||||
case reflect.Float32, reflect.Float64:
|
||||
less = func(a, b reflect.Value) bool { return a.Float() < b.Float() }
|
||||
case reflect.String:
|
||||
less = func(a, b reflect.Value) bool { return a.String() < b.String() }
|
||||
case reflect.Bool:
|
||||
less = 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 := v.Index(i)
|
||||
b := v.Index(j)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
a = a.Elem()
|
||||
b = b.Elem()
|
||||
}
|
||||
a = a.FieldByIndex(sf.Index)
|
||||
b = b.FieldByIndex(sf.Index)
|
||||
return less(a, b)
|
||||
})
|
||||
|
||||
if sortType[0] == "desc" {
|
||||
ReverseSlice(slice)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
438
slice/slice_test.go
Normal file
438
slice/slice_test.go
Normal file
@@ -0,0 +1,438 @@
|
||||
package slice
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/utils"
|
||||
)
|
||||
|
||||
func TestContain(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c", "d"}
|
||||
contain(t, t1, "a", true)
|
||||
contain(t, t1, "e", false)
|
||||
|
||||
var t2 []string
|
||||
contain(t, t2, "1", false)
|
||||
}
|
||||
|
||||
func contain(t *testing.T, test interface{}, value interface{}, expected bool) {
|
||||
res := Contain(test, value)
|
||||
if res != expected {
|
||||
utils.LogFailedTestInfo(t, "Contain", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestChunk(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
r1 := [][]interface{}{
|
||||
{"a"},
|
||||
{"b"},
|
||||
{"c"},
|
||||
{"d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 1, r1)
|
||||
|
||||
r2 := [][]interface{}{
|
||||
{"a", "b"},
|
||||
{"c", "d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 2, r2)
|
||||
|
||||
r3 := [][]interface{}{
|
||||
{"a", "b", "c"},
|
||||
{"d", "e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 3, r3)
|
||||
|
||||
r4 := [][]interface{}{
|
||||
{"a", "b", "c", "d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 4, r4)
|
||||
|
||||
r5 := [][]interface{}{
|
||||
{"a"},
|
||||
{"b"},
|
||||
{"c"},
|
||||
{"d"},
|
||||
{"e"},
|
||||
}
|
||||
chunk(t, InterfaceSlice(t1), 5, r5)
|
||||
|
||||
}
|
||||
|
||||
func chunk(t *testing.T, test []interface{}, num int, expected [][]interface{}) {
|
||||
res := Chunk(test, num)
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "Chunk", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertSlice(t *testing.T) {
|
||||
//t1 := []string{"1","2"}
|
||||
//aInt, _ := strconv.ParseInt("1", 10, 64)
|
||||
//bInt, _ := strconv.ParseInt("2", 10, 64)
|
||||
//expected :=[]int64{aInt, bInt}
|
||||
//
|
||||
//a := ConvertSlice(t1, reflect.TypeOf(expected))
|
||||
//if !reflect.DeepEqual(a, expected) {
|
||||
// utils.LogFailedTestInfo(t, "ConvertSlice", t1, expected, a)
|
||||
// t.FailNow()
|
||||
//}
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4, 5}
|
||||
even := func(i, num int) bool {
|
||||
return num%2 == 0
|
||||
}
|
||||
e1 := []int{2, 4}
|
||||
r1 := Filter(s1, even)
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Filter", s1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
type student struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
students := []student{
|
||||
{"a", 10},
|
||||
{"b", 11},
|
||||
{"c", 12},
|
||||
{"d", 13},
|
||||
{"e", 14},
|
||||
}
|
||||
|
||||
e2 := []student{
|
||||
{"d", 13},
|
||||
{"e", 14},
|
||||
}
|
||||
filterFunc := func(i int, s student) bool {
|
||||
return s.age > 12
|
||||
}
|
||||
|
||||
r2 := Filter(students, filterFunc)
|
||||
if !reflect.DeepEqual(r2, e2) {
|
||||
utils.LogFailedTestInfo(t, "Filter", students, e2, r2)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
multiplyTwo := func(i, num int) int {
|
||||
return num * 2
|
||||
}
|
||||
e1 := []int{2, 4, 6, 8}
|
||||
r1 := Map(s1, multiplyTwo)
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Map", s1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
type student struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
students := []student{
|
||||
{"a", 1},
|
||||
{"b", 2},
|
||||
{"c", 3},
|
||||
}
|
||||
|
||||
e2 := []student{
|
||||
{"a", 11},
|
||||
{"b", 12},
|
||||
{"c", 13},
|
||||
}
|
||||
mapFunc := func(i int, s student) student {
|
||||
s.age += 10
|
||||
return s
|
||||
}
|
||||
r2 := Map(students, mapFunc)
|
||||
if !reflect.DeepEqual(r2, e2) {
|
||||
utils.LogFailedTestInfo(t, "Filter", students, e2, r2)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
f1 := func(i, v1, v2 int) int {
|
||||
return v1 + v2
|
||||
}
|
||||
e1 := 10
|
||||
r1 := Reduce(s1, f1, 0)
|
||||
if e1 != r1 {
|
||||
utils.LogFailedTestInfo(t, "Reduce", s1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// failed Reduce function should be func(i int, v1, v2 int) int
|
||||
//s1 := []int{1, 2, 3, 4}
|
||||
//f1 := func(i string, v1, v2 int) int { //i should be int
|
||||
// return v1+v2
|
||||
//}
|
||||
//e1 := 10
|
||||
//r1 := Reduce(s1, f1, 0)
|
||||
//if e1 != r1 {
|
||||
// utils.LogFailedTestInfo(t, "Reduce", s1, e1, r1)
|
||||
// t.FailNow()
|
||||
//}
|
||||
}
|
||||
|
||||
func TestIntSlice(t *testing.T) {
|
||||
var t1 []interface{}
|
||||
t1 = append(t1, 1, 2, 3, 4, 5)
|
||||
expect := []int{1, 2, 3, 4, 5}
|
||||
intSlice(t, t1, expect)
|
||||
}
|
||||
|
||||
func intSlice(t *testing.T, test interface{}, expected []int) {
|
||||
res, err := IntSlice(test)
|
||||
if err != nil {
|
||||
t.Error("IntSlice Error: " + err.Error())
|
||||
}
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "IntSlice", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSlice(t *testing.T) {
|
||||
var t1 []interface{}
|
||||
t1 = append(t1, "a", "b", "c", "d", "e")
|
||||
expect := []string{"a", "b", "c", "d", "e"}
|
||||
stringSlice(t, t1, expect)
|
||||
}
|
||||
|
||||
func stringSlice(t *testing.T, test interface{}, expected []string) {
|
||||
res := StringSlice(test)
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "StringSlice", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceSlice(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c", "d", "e"}
|
||||
expect := []interface{}{"a", "b", "c", "d", "e"}
|
||||
interfaceSlice(t, t1, expect)
|
||||
}
|
||||
|
||||
func interfaceSlice(t *testing.T, test interface{}, expected []interface{}) {
|
||||
res := InterfaceSlice(test)
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "InterfaceSlice", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteByIndex(t *testing.T) {
|
||||
origin := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
t1 := []string{"a", "b", "c", "d", "e"}
|
||||
r1 := []string{"b", "c", "d", "e"}
|
||||
deleteByIndex(t, origin, t1, 0, 0, r1)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r2 := []string{"a", "b", "c", "e"}
|
||||
deleteByIndex(t, origin, t1, 3, 0, r2)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r3 := []string{"a", "b", "c", "d"}
|
||||
deleteByIndex(t, origin, t1, 4, 0, r3)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r4 := []string{"c", "d", "e"}
|
||||
deleteByIndex(t, origin, t1, 0, 2, r4)
|
||||
|
||||
t1 = []string{"a", "b", "c", "d", "e"}
|
||||
r5 := []string{} // var r5 []string{} failed
|
||||
deleteByIndex(t, origin, t1, 0, 5, r5)
|
||||
|
||||
// failed
|
||||
//t1 = []string{"a", "b", "c", "d","e"}
|
||||
//r6 := []string{"a", "c", "d","e"}
|
||||
//deleteByIndex(t, origin, t1, 1, 1, r6)
|
||||
|
||||
// failed
|
||||
//t1 = []string{"a", "b", "c", "d","e"}
|
||||
//r7 := []string{}
|
||||
//deleteByIndex(t, origin, t1, 0, 6, r7)
|
||||
}
|
||||
|
||||
func deleteByIndex(t *testing.T, origin, test interface{}, start, end int, expected interface{}) {
|
||||
var res interface{}
|
||||
var err error
|
||||
if end != 0 {
|
||||
res, err = DeleteByIndex(test, start, end)
|
||||
} else {
|
||||
res, err = DeleteByIndex(test, start)
|
||||
}
|
||||
if err != nil {
|
||||
t.Error("DeleteByIndex Error: " + err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "DeleteByIndex", origin, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertByIndex(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c"}
|
||||
|
||||
r1 := []string{"1", "a", "b", "c"}
|
||||
insertByIndex(t, t1, 0, "1", r1)
|
||||
|
||||
r2 := []string{"a", "1", "b", "c"}
|
||||
insertByIndex(t, t1, 1, "1", r2)
|
||||
|
||||
r3 := []string{"a", "b", "c", "1"}
|
||||
insertByIndex(t, t1, 3, "1", r3)
|
||||
|
||||
r4 := []string{"1", "2", "3", "a", "b", "c"}
|
||||
insertByIndex(t, t1, 0, []string{"1", "2", "3"}, r4)
|
||||
|
||||
r5 := []string{"a", "1", "2", "3", "b", "c"}
|
||||
insertByIndex(t, t1, 1, []string{"1", "2", "3"}, r5)
|
||||
|
||||
r6 := []string{"a", "b", "1", "2", "3", "c"}
|
||||
insertByIndex(t, t1, 2, []string{"1", "2", "3"}, r6)
|
||||
|
||||
r7 := []string{"a", "b", "c", "1", "2", "3"}
|
||||
insertByIndex(t, t1, 3, []string{"1", "2", "3"}, r7)
|
||||
}
|
||||
|
||||
func insertByIndex(t *testing.T, test interface{}, index int, value, expected interface{}) {
|
||||
res, err := InsertByIndex(test, index, value)
|
||||
if err != nil {
|
||||
t.Error("InsertByIndex Error: " + err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "InsertByIndex", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateByIndex(t *testing.T) {
|
||||
t1 := []string{"a", "b", "c"}
|
||||
r1 := []string{"1", "b", "c"}
|
||||
updateByIndex(t, t1, 0, "1", r1)
|
||||
|
||||
t1 = []string{"a", "b", "c"}
|
||||
r2 := []string{"a", "1", "c"}
|
||||
updateByIndex(t, t1, 1, "1", r2)
|
||||
|
||||
t1 = []string{"a", "b", "c"}
|
||||
r3 := []string{"a", "b", "1"}
|
||||
updateByIndex(t, t1, 2, "1", r3)
|
||||
|
||||
//failed
|
||||
//t1 = []string{"a","b","c"}
|
||||
//r4 := []string{"a", "b", "1"}
|
||||
//updateByIndex(t, t1, 3, "1", r4)
|
||||
|
||||
}
|
||||
|
||||
func updateByIndex(t *testing.T, test interface{}, index int, value, expected interface{}) {
|
||||
res, err := UpdateByIndex(test, index, value)
|
||||
if err != nil {
|
||||
t.Error("UpdateByIndex Error: " + err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
utils.LogFailedTestInfo(t, "UpdateByIndex", test, expected, res)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnique(t *testing.T) {
|
||||
t1 := []int{1, 2, 2, 3}
|
||||
e1 := []int{1, 2, 3}
|
||||
r1, _ := IntSlice(Unique(t1))
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Unique", t1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
t2 := []string{"a", "a", "b", "c"}
|
||||
e2 := []string{"a", "b", "c"}
|
||||
r2 := StringSlice(Unique(t2))
|
||||
if !reflect.DeepEqual(r2, e2) {
|
||||
utils.LogFailedTestInfo(t, "Unique", t2, e2, r2)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseSlice(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4, 5}
|
||||
e1 := []int{5, 4, 3, 2, 1}
|
||||
ReverseSlice(s1)
|
||||
if !reflect.DeepEqual(s1, e1) {
|
||||
utils.LogFailedTestInfo(t, "ReverseSlice", s1, e1, s1)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
s2 := []string{"a", "b", "c", "d", "e"}
|
||||
e2 := []string{"e", "d", "c", "b", "a"}
|
||||
ReverseSlice(s2)
|
||||
if !reflect.DeepEqual(s2, e2) {
|
||||
utils.LogFailedTestInfo(t, "ReverseSlice", s2, e2, s2)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDifference(t *testing.T) {
|
||||
s1 := []int{1, 2, 3, 4, 5}
|
||||
s2 := []int{4, 5, 6}
|
||||
e1 := []int{1, 2, 3}
|
||||
r1 := Difference(s1, s2)
|
||||
if !reflect.DeepEqual(r1, e1) {
|
||||
utils.LogFailedTestInfo(t, "Difference", s1, e1, r1)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortByField(t *testing.T) {
|
||||
type student struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
students := []student{
|
||||
{"a", 10},
|
||||
{"b", 15},
|
||||
{"c", 5},
|
||||
{"d", 6},
|
||||
}
|
||||
|
||||
sortByAge := []student{
|
||||
{"b", 15},
|
||||
{"a", 10},
|
||||
{"d", 6},
|
||||
{"c", 5},
|
||||
}
|
||||
|
||||
err := SortByField(students, "age", "desc")
|
||||
if err != nil {
|
||||
t.Error("IntSlice Error: " + err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(students, sortByAge) {
|
||||
utils.LogFailedTestInfo(t, "SortByField", students, sortByAge, students)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
}
|
||||
54
slice/slice_util.go
Normal file
54
slice/slice_util.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package slice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// sliceValue return the reflect value of a slice
|
||||
func sliceValue(slice interface{}) 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 interface{}) 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
|
||||
}
|
||||
Reference in New Issue
Block a user