feat: 优化 slice 包 Filter 和 Map 函数,新增 Reduce 函数

This commit is contained in:
kercylan98 2023-09-07 20:05:01 +08:00
parent a4ba3f1fa8
commit 5ab990246d
6 changed files with 160 additions and 22 deletions

64
utils/slice/filter.go Normal file
View File

@ -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)
}

View File

@ -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)
}
}

14
utils/slice/map.go Normal file
View File

@ -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
}

28
utils/slice/reduce.go Normal file
View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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
}