diff --git a/README.md b/README.md index c0a16d3..dc88af4 100644 --- a/README.md +++ b/README.md @@ -396,8 +396,8 @@ func Contain[T comparable](slice []T, value T) bool //check if the value is in t func Chunk[T any](slice []T, size int) [][]T //creates an slice of elements split into groups the length of size. func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} //convert originalSlice to newSliceType func Difference[T comparable](slice1, slice2 []T) []T //creates an slice of whose element not included in the other given slice -func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //delete the element of slice from start index to end index - 1 -func Drop(slice interface{}, n int) interface{} //creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0 +func DeleteByIndex[T any](slice []T, start int, end ...int) []T //delete the element of slice from start index to end index - 1 +func Drop[T any](slice []T, n int) []T //creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0 func Every[T any](slice []T, fn func(index int, t T) bool) bool //return true if all of the values in the slice pass the predicate function func None[T any](slice []T, fn func(index int, t T) bool) bool // return true if all the values in the slice mismatch the criteria func Filter [T any] (slice []T, fn func(index int, t T) bool) []T //filter slice, fn signature should be func(int, T) bool. @@ -406,8 +406,8 @@ func FlattenDeep(slice interface{}) interface{} //flattens slice recursive func ForEach [T any] (slice []T, fn func(index int, t T)) //iterates over elements of slice and invokes function for each element, fn signature should be func(int, T ). func IntSlice(slice interface{}) ([]int, error) //convert value to int slice func InterfaceSlice(slice interface{}) []interface{} //convert value to interface{} slice -func Intersection(slices ...interface{}) interface{} //creates a slice of unique values that included by all slices. -func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //insert the element into slice at index. +func Intersection[T comparable](slices ...[]T) []T //creates a slice of unique values that included by all slices. +func InsertByIndex[T any](slice []T, index int, value interface{}) []T //insert the value or other slice into slice at index. func Map [T any, U any] (slice []T, fn func(index int, t T) U) []U //map lisce, fn signature should be func(int, T). func Reverse[T any](slice []T)//revere slice func Reduce[T any](slice []T, fn func(index int, t1, t2 T) T, initial T) T //reduce slice @@ -415,10 +415,10 @@ func Shuffle[T any](slice []T) []T //creates an slice of shuffled values func SortByField(slice interface{}, field string, sortType ...string) error //sort struct slice by field func Some[T any](slice []T, fn func(index int, t T) bool) bool //return true if any of the values in the list pass the predicate fn function func StringSlice(slice interface{}) []string //convert value to string slice -func Unique(slice interface{}) interface{} //remove duplicate elements in slice -func Union(slices ...interface{}) interface{} //Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons. -func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //update the slice element at index. -func Without(slice interface{}, values ...interface{}) interface{} //creates a slice excluding all given values +func Unique[T comparable](slice []T) []T //remove duplicate elements in slice +func Union[T comparable](slices ...[]T) []T //Union creates a slice of unique values, in order, from all given slices. using == for equality comparisons. +func UpdateByIndex[T any](slice []T, index int, value T) []T //update the slice element at index. +func Without[T comparable](slice []T, values ...T) []T //creates a slice excluding all given values func GroupBy(slice, function interface{}) (interface{}, interface{}) // groups slice into two categories func Count[T any](slice []T, fn func(index int, t T) bool) int // Count iterates over elements of slice, returns a count of all matched elements ``` diff --git a/README_zh-CN.md b/README_zh-CN.md index ca085b1..4dd04de 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -397,8 +397,8 @@ func Contain[T comparable](slice []T, value T) bool //判断slice是否包含val func Chunk[T any](slice []T, size int) [][]T //均分slice func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interface{} //将originalSlice转换为 newSliceType func Difference[T comparable](slice1, slice2 []T) []T //返回切片,其元素在slice1中,不在slice2中 -func DeleteByIndex(slice interface{}, start int, end ...int) (interface{}, error) //删除切片中start到end位置的值 -func Drop(slice interface{}, n int) interface{} //创建一个新切片,当n大于0时删除原切片前n个元素,当n小于0时删除原切片后n个元素 +func DeleteByIndex[T any](slice []T, start int, end ...int) []T //删除切片中start到end位置的值(不包含end) +func Drop[T any](slice []T, n int) []T //创建一个新切片,当n大于0时删除原切片前n个元素,当n小于0时删除原切片后n个元素 func Every[T any](slice []T, fn func(index int, t T) bool) bool //slice中所有元素都符合函数条件时返回true, 否则返回false. 函数签名:func(index int, t T) bool func None[T any](slice []T, fn func(index int, t T) bool) bool //slice中所有元素都不符合函数条件时返回true, 否则返回false. 函数签名:func(index int, value interface{}) bool func Find[T any](slice []T, fn func(index int, t T) bool) (*T, bool)//查找slice中第一个符合条件的元素,函数签名:func(index int, value interface{}) bool @@ -407,19 +407,19 @@ func FlattenDeep(slice interface{}) interface{} //将slice递归为一维切片 func ForEach [T any] (slice []T, fn func(index int, t T)) //遍历切片,在每个元素上执行函数 func IntSlice(slice interface{}) ([]int, error) //转成int切片 func InterfaceSlice(slice interface{}) []interface{} //转成interface{}切片 -func Intersection(slices ...interface{}) interface{} //slice交集,去重 -func InsertByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置插入value -func Map(slice, function interface{}) interface{} //遍历切片, 函数签名:func(index int, value interface{}) interface{} +func Intersection[T comparable](slices ...[]T) []T //slice交集,去重 +func InsertByIndex[T any](slice []T, index int, value interface{}) []T //在切片中index位置插入value +func Map [T any, U any] (slice []T, fn func(index int, t T) U) []U //遍历切片 func Reverse[T any](slice []T) //反转切片 func Reduce[T any](slice []T, fn func(index int, t1, t2 T) T, initial T) T //切片reduce操作 func Shuffle[T any](slice []T) []T //创建一个被打乱值的切片 func Some[T any](slice []T, fn func(index int, t T) bool) bool //slice中任意一个元素都符合函数条件时返回true, 否则返回false. func SortByField(slice interface{}, field string, sortType ...string) error //对struct切片进行排序 func StringSlice(slice interface{}) []string //转为string切片 -func Unique(slice interface{}) interface{} //去重切片 -func Union(slices ...interface{}) interface{} //slice并集, 去重 -func UpdateByIndex(slice interface{}, index int, value interface{}) (interface{}, error) //在切片中index位置更新value -func Without(slice interface{}, values ...interface{}) interface{} //slice去除values +func Unique[T comparable](slice []T) []T //去重切片 +func Union[T comparable](slices ...[]T) []T //slice并集, 去重 +func UpdateByIndex[T any](slice []T, index int, value T) []T //在切片中index位置更新value +func Without[T comparable](slice []T, values ...T) []T //slice去除values func GroupBy(slice, function interface{}) (interface{}, interface{}) //根据函数function的逻辑分slice为两组slice func Count[T any](slice []T, fn func(index int, t T) bool) int //遍历slice的元素,返回所有匹配元素的计数 ``` diff --git a/slice/slice.go b/slice/slice.go index 59b2f2b..a8089ba 100644 --- a/slice/slice.go +++ b/slice/slice.go @@ -5,7 +5,6 @@ package slice import ( - "errors" "fmt" "math" "math/rand" @@ -216,7 +215,7 @@ func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value { } // ForEach iterates over elements of slice and invokes function for each element -// The fn signature should be func(int, T). +// The fn function signature should be func(int, T). func ForEach[T any](slice []T, fn func(index int, t T)) { for i, v := range slice { fn(i, v) @@ -224,7 +223,7 @@ func ForEach[T any](slice []T, fn func(index int, t T)) { } // Map creates an slice of values by running each element of slice thru fn function. -// The fn signature should be func(int, T). +// 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 { res := make([]U, len(slice), cap(slice)) for i, v := range slice { @@ -321,107 +320,87 @@ func ConvertSlice(originalSlice interface{}, newSliceType reflect.Type) interfac } // 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())) - } +func DeleteByIndex[T any](slice []T, start int, end ...int) []T { + size := len(slice) - return v.Interface(), nil -} - -// 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(slice interface{}, n int) interface{} { - sv := sliceValue(slice) - - if n == 0 { + if start < 0 || start > size-1 { return slice } - svLen := sv.Len() - - if math.Abs(float64(n)) >= float64(svLen) { - return reflect.MakeSlice(sv.Type(), 0, 0).Interface() - } - - if n > 0 { - res := reflect.MakeSlice(sv.Type(), svLen-n, svLen-n) - for i := 0; i < res.Len(); i++ { - res.Index(i).Set(sv.Index(i + n)) + if len(end) > 0 { + end := end[0] + if end <= start { + return slice + } + if end > size { + end = size } - return res.Interface() + slice = append(slice[:start], slice[end:]...) + return slice } - res := reflect.MakeSlice(sv.Type(), svLen+n, svLen+n) - for i := 0; i < res.Len(); i++ { - res.Index(i).Set(sv.Index(i)) + if start == size-1 { + slice = append(slice[:start]) + } else { + slice = append(slice[:start], slice[start+1:]...) } - return res.Interface() + return slice } -// 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) +// 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 index < 0 || index > v.Len() { - return slice, errors.New("InvalidSliceIndex") + if size == 0 || n == 0 { + return slice } - // 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 + if math.Abs(float64(n)) >= float64(size) { + return []T{} } - // 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 + if n < 0 { + return slice[0 : size+n] } - v = reflect.AppendSlice(v.Slice(0, index+1), v.Slice(index, v.Len())) - v.Index(index).Set(reflect.ValueOf(value)) + return slice[n:size] +} - return v.Interface(), nil +// 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(slice interface{}, index int, value interface{}) (interface{}, error) { - v := sliceValue(slice) +func UpdateByIndex[T any](slice []T, index int, value T) []T { + size := len(slice) - if index < 0 || index >= v.Len() { - return slice, errors.New("InvalidSliceIndex") + if index < 0 || index >= size { + return slice } + slice = append(slice[:index], append([]T{value}, slice[index+1:]...)...) - if reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) { - return slice, errors.New("InvalidValueType") - } - - v.Index(index).Set(reflect.ValueOf(value)) - - return v.Interface(), nil + return slice } // Unique remove duplicate elements in slice. @@ -587,24 +566,18 @@ func reverseSlice(slice interface{}) { } // Without creates a slice excluding all given values -func Without(slice interface{}, values ...interface{}) interface{} { - sv := sliceValue(slice) - if sv.Len() == 0 { - return slice - } - +func Without[T comparable](slice []T, values ...T) []T { var indexes []int - for i := 0; i < sv.Len(); i++ { - v := sv.Index(i).Interface() - if !Contain(values, v) { + for i := 0; i < len(slice); i++ { + if !Contain(values, slice[i]) { 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])) + res := make([]T, len(indexes), len(indexes)) + for i, v := range indexes { + res[i] = slice[v] } - return res.Interface() + return res } diff --git a/slice/slice_test.go b/slice/slice_test.go index a252723..9a680b6 100644 --- a/slice/slice_test.go +++ b/slice/slice_test.go @@ -263,32 +263,18 @@ func TestInterfaceSlice(t *testing.T) { func TestDeleteByIndex(t *testing.T) { assert := internal.NewAssert(t, "TestDeleteByIndex") - t1 := []string{"a", "b", "c", "d", "e"} - r1 := []string{"b", "c", "d", "e"} - a1, _ := DeleteByIndex(t1, 0) - assert.Equal(r1, a1) + assert.Equal([]string{"a", "b", "c"}, DeleteByIndex([]string{"a", "b", "c"}, -1)) + assert.Equal([]string{"a", "b", "c"}, DeleteByIndex([]string{"a", "b", "c"}, 3)) + assert.Equal([]string{"b", "c"}, DeleteByIndex([]string{"a", "b", "c"}, 0)) + assert.Equal([]string{"a", "c"}, DeleteByIndex([]string{"a", "b", "c"}, 1)) + assert.Equal([]string{"a", "b"}, DeleteByIndex([]string{"a", "b", "c"}, 2)) - t2 := []string{"a", "b", "c", "d", "e"} - r2 := []string{"a", "b", "c", "e"} - a2, _ := DeleteByIndex(t2, 3) - assert.Equal(r2, a2) - - t3 := []string{"a", "b", "c", "d", "e"} - r3 := []string{"c", "d", "e"} - a3, _ := DeleteByIndex(t3, 0, 2) - assert.Equal(r3, a3) - - t4 := []string{"a", "b", "c", "d", "e"} - r4 := []string{} - a4, _ := DeleteByIndex(t4, 0, 5) - assert.Equal(r4, a4) - - t5 := []string{"a", "b", "c", "d", "e"} - _, err := DeleteByIndex(t5, 1, 1) - assert.IsNotNil(err) - - _, err = DeleteByIndex(t5, 0, 6) - assert.IsNotNil(err) + assert.Equal([]string{"b", "c"}, DeleteByIndex([]string{"a", "b", "c"}, 0, 1)) + assert.Equal([]string{"c"}, DeleteByIndex([]string{"a", "b", "c"}, 0, 2)) + assert.Equal([]string{}, DeleteByIndex([]string{"a", "b", "c"}, 0, 3)) + assert.Equal([]string{}, DeleteByIndex([]string{"a", "b", "c"}, 0, 4)) + assert.Equal([]string{"a"}, DeleteByIndex([]string{"a", "b", "c"}, 1, 3)) + assert.Equal([]string{"a"}, DeleteByIndex([]string{"a", "b", "c"}, 1, 4)) } func TestDrop(t *testing.T) { @@ -311,45 +297,23 @@ func TestDrop(t *testing.T) { func TestInsertByIndex(t *testing.T) { assert := internal.NewAssert(t, "TestInsertByIndex") - t1 := []string{"a", "b", "c"} - r1, _ := InsertByIndex(t1, 0, "1") - assert.Equal([]string{"1", "a", "b", "c"}, r1) - - r2, _ := InsertByIndex(t1, 1, "1") - assert.Equal([]string{"a", "1", "b", "c"}, r2) - - r3, _ := InsertByIndex(t1, 3, "1") - assert.Equal([]string{"a", "b", "c", "1"}, r3) - - r4, _ := InsertByIndex(t1, 0, []string{"1", "2", "3"}) - assert.Equal([]string{"1", "2", "3", "a", "b", "c"}, r4) - - r5, _ := InsertByIndex(t1, 3, []string{"1", "2", "3"}) - assert.Equal([]string{"a", "b", "c", "1", "2", "3"}, r5) - - _, err := InsertByIndex(t1, 4, "1") - assert.IsNotNil(err) - - _, err = InsertByIndex(t1, 0, 1) - assert.IsNotNil(err) + strs := []string{"a", "b", "c"} + assert.Equal([]string{"a", "b", "c"}, InsertByIndex(strs, -1, "1")) + assert.Equal([]string{"a", "b", "c"}, InsertByIndex(strs, 4, "1")) + assert.Equal([]string{"1", "a", "b", "c"}, InsertByIndex(strs, 0, "1")) + assert.Equal([]string{"a", "b", "c", "1"}, InsertByIndex(strs, 3, "1")) + assert.Equal([]string{"1", "2", "3", "a", "b", "c"}, InsertByIndex(strs, 0, []string{"1", "2", "3"})) + assert.Equal([]string{"a", "b", "c", "1", "2", "3"}, InsertByIndex(strs, 3, []string{"1", "2", "3"})) + t.Log(strs) } func TestUpdateByIndex(t *testing.T) { assert := internal.NewAssert(t, "TestUpdateByIndex") - t1 := []string{"a", "b", "c"} - r1, _ := UpdateByIndex(t1, 0, "1") - assert.Equal([]string{"1", "b", "c"}, r1) - - t2 := []string{"a", "b", "c"} - r2, _ := UpdateByIndex(t2, 1, "1") - assert.Equal([]string{"a", "1", "c"}, r2) - - _, err := UpdateByIndex([]string{"a", "b", "c"}, 4, "1") - assert.IsNotNil(err) - - _, err = UpdateByIndex([]string{"a", "b", "c"}, 0, 1) - assert.IsNotNil(err) + assert.Equal([]string{"a", "b", "c"}, UpdateByIndex([]string{"a", "b", "c"}, -1, "1")) + assert.Equal([]string{"1", "b", "c"}, UpdateByIndex([]string{"a", "b", "c"}, 0, "1")) + assert.Equal([]string{"a", "b", "2"}, UpdateByIndex([]string{"a", "b", "c"}, 2, "2")) + assert.Equal([]string{"a", "b", "c"}, UpdateByIndex([]string{"a", "b", "c"}, 3, "2")) } func TestUnique(t *testing.T) { @@ -418,7 +382,7 @@ func TestDifference(t *testing.T) { } func TestSortByField(t *testing.T) { - assert := internal.NewAssert(t, "TestWithout") + assert := internal.NewAssert(t, "TestSortByField") type student struct { name string