diff --git a/utils/slice/filter.go b/utils/slice/filter.go new file mode 100644 index 0000000..bae96ca --- /dev/null +++ b/utils/slice/filter.go @@ -0,0 +1,64 @@ +package slice + +// Filter 根据特定的表达式过滤切片成员 +// - reserve: 是否保留符合条件的成员 +// - slice: 待过滤的切片 +// - expression: 过滤表达式 +// +// 这个函数的作用是遍历输入切片 slice,然后根据 expression 函数的返回值来决定是否保留每个元素。具体来说 +// - 如果 expression 返回 true 并且 reserve 也是 true,那么元素会被保留 +// - 如果 expression 返回 false 并且 reserve 是 false,那么元素也会被保留 +// +// 该没有创建新的内存空间或进行元素复制,所以整个操作相当高效。同时,由于 s 和 slice 实际上共享底层的数组,因此这个函数会改变传入的 slice。如果不希望改变原切片,需要在函数调用之前手动复制一份或者使用 FilterCopy 函数。 +func Filter[V any](reserve bool, slice []V, expression func(index int, item V) bool) []V { + if len(slice) == 0 { + return slice + } + + var ( + i int + j int + ) + for i = 0; i < len(slice); i++ { + if expression(i, slice[i]) { + if reserve { + slice[j] = slice[i] + j++ + } + } else { + if !reserve { + slice[j] = slice[i] + j++ + } + } + } + + return slice[:j] +} + +// FilterT 与 Filter 的功能相同,但是 reserve 被默认为 true +func FilterT[V any](slice []V, expression func(index int, item V) bool) []V { + return Filter(true, slice, expression) +} + +// FilterF 与 Filter 的功能相同,但是 reserve 被默认为 false +func FilterF[V any](slice []V, expression func(index int, item V) bool) []V { + return Filter(false, slice, expression) +} + +// FilterCopy 与 Filter 的功能相同,但是不会改变原切片,而是返回一个新的切片 +func FilterCopy[V any](reserve bool, slice []V, expression func(index int, item V) bool) []V { + var s = make([]V, len(slice)) + copy(s, slice) + return Filter(reserve, s, expression) +} + +// FilterCopyT 与 FilterCopy 的功能相同,但是 reserve 被默认为 true +func FilterCopyT[V any](slice []V, expression func(index int, item V) bool) []V { + return FilterCopy(true, slice, expression) +} + +// FilterCopyF 与 FilterCopy 的功能相同,但是 reserve 被默认为 false +func FilterCopyF[V any](slice []V, expression func(index int, item V) bool) []V { + return FilterCopy(false, slice, expression) +} diff --git a/utils/slice/filter_test.go b/utils/slice/filter_test.go new file mode 100644 index 0000000..1249821 --- /dev/null +++ b/utils/slice/filter_test.go @@ -0,0 +1,34 @@ +package slice_test + +import ( + "github.com/kercylan98/minotaur/utils/slice" + "testing" +) + +func TestFilter(t *testing.T) { + s := []int{1, 2, 3, 4, 5} + s = slice.Filter(true, s, func(index int, item int) bool { + return item%2 == 0 + }) + if len(s) != 2 { + t.Error("Filter failed") + } + if s[0] != 2 { + t.Error("Filter failed") + } + if s[1] != 4 { + t.Error("Filter failed") + } +} + +func TestFilterCopy(t *testing.T) { + s := []int{1, 2, 3, 4, 5} + cp := slice.FilterCopy(true, s, func(index int, item int) bool { + return item%2 == 0 + }) + if len(s) != 5 { + t.Error("FilterCopy failed") + } else { + t.Log(s, cp) + } +} diff --git a/utils/slice/map.go b/utils/slice/map.go new file mode 100644 index 0000000..9d3af4a --- /dev/null +++ b/utils/slice/map.go @@ -0,0 +1,14 @@ +package slice + +// Map 将切片中的每一个元素转换为另一种类型的元素 +// - slice: 待转换的切片 +// - converter: 转换函数 +// +// 这不会改变原有的切片,而是返回一个新的切片 +func Map[V any, T any](slice []V, converter func(index int, item V) T) []T { + var s = make([]T, len(slice)) + for i := 0; i < len(slice); i++ { + s[i] = converter(i, slice[i]) + } + return s +} diff --git a/utils/slice/reduce.go b/utils/slice/reduce.go new file mode 100644 index 0000000..ae31edd --- /dev/null +++ b/utils/slice/reduce.go @@ -0,0 +1,28 @@ +package slice + +import ( + "github.com/kercylan98/minotaur/utils/generic" +) + +// Reduce 将切片中的多个元素组合成一个单一值 +// - start: 开始索引,如果为负数则从后往前计算,例如:-1 表示从最后一个元素开始向左遍历,1 表示从第二个元素开始 +// - slice: 待组合的切片 +// - reducer: 组合函数 +func Reduce[V any, R generic.Number](start int, slice []V, reducer func(index int, item V, current R) R) (result R) { + length := len(slice) + + if start >= length || -start > length { + return + } + + if start < 0 { + for i := length + start; i >= 0; i-- { + result = reducer(i, slice[i], result) + } + } else { + for i := start; i < length; i++ { + result = reducer(i, slice[i], result) + } + } + return result +} diff --git a/utils/slice/reduce_test.go b/utils/slice/reduce_test.go new file mode 100644 index 0000000..3553fd9 --- /dev/null +++ b/utils/slice/reduce_test.go @@ -0,0 +1,17 @@ +package slice_test + +import ( + "github.com/kercylan98/minotaur/utils/slice" + "testing" +) + +func TestReduce(t *testing.T) { + s := []int{1, 2, 3, 4, 5} + sum := slice.Reduce(0, s, func(index int, item int, current int) int { + return current + item + }) + t.Log(sum) + if sum != 15 { + t.Error("Reduce failed") + } +} diff --git a/utils/slice/slice.go b/utils/slice/slice.go index 90f3473..23c386f 100644 --- a/utils/slice/slice.go +++ b/utils/slice/slice.go @@ -5,6 +5,9 @@ import ( "reflect" ) +// Slice 切片类型 +type Slice[V any] []V + // GetValue 获取特定索引的元素,如果索引超出范围则返回零值 func GetValue[V any](slice []V, i int) (v V) { if i >= 0 && i < len(slice) { @@ -333,25 +336,3 @@ func SubWithCheck[T any](a, b []T, checkHandle func(a, b T) bool) []T { } return result } - -// Filter 过滤切片中的元素 -// - filterHandle 返回 true 表示需要保留 -func Filter[T any](a []T, filterHandle func(a T) bool) []T { - var result []T - for _, a := range a { - if filterHandle(a) { - result = append(result, a) - } - } - return result -} - -// Mapping 将切片中的元素进行转换 -// - mappingHandle 返回转换后的元素 -func Mapping[T any, R any](a []T, mappingHandle func(a T) R) []R { - var result []R - for _, a := range a { - result = append(result, mappingHandle(a)) - } - return result -}