From d7724094d19943303b9bbe2b61fa8cb3e595c7c8 Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Fri, 8 Sep 2023 13:13:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=A4=A7=E9=87=8F=20?= =?UTF-8?q?slice=20=E5=8C=85=E5=92=8C=20hash=20=E5=8C=85=E7=9A=84=E8=BE=85?= =?UTF-8?q?=E5=8A=A9=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/hash/chunk.go | 27 +++++++++++++ utils/hash/drop.go | 56 ++++++++++++++++++++++++++ utils/hash/each.go | 47 ++++++++++++++++++++++ utils/hash/filter.go | 56 ++++++++++++++++++++++++++ utils/slice/chunk.go | 18 +++++++++ utils/slice/drop.go | 40 +++++++++++++++++++ utils/slice/drop_test.go | 11 ++++++ utils/slice/each.go | 85 ++++++++++++++++++++++++++++++++++++++++ utils/slice/fill.go | 58 +++++++++++++++++++++++++++ utils/slice/filter.go | 8 ++-- utils/slice/group.go | 17 ++++++++ utils/slice/shuffle.go | 30 ++++++++++++++ utils/slice/slice.go | 9 ----- utils/slice/unique.go | 44 +++++++++++++++++++++ 14 files changed, 493 insertions(+), 13 deletions(-) create mode 100644 utils/hash/chunk.go create mode 100644 utils/hash/drop.go create mode 100644 utils/hash/each.go create mode 100644 utils/hash/filter.go create mode 100644 utils/slice/chunk.go create mode 100644 utils/slice/drop.go create mode 100644 utils/slice/drop_test.go create mode 100644 utils/slice/each.go create mode 100644 utils/slice/fill.go create mode 100644 utils/slice/group.go create mode 100644 utils/slice/shuffle.go create mode 100644 utils/slice/unique.go diff --git a/utils/hash/chunk.go b/utils/hash/chunk.go new file mode 100644 index 0000000..0f690c8 --- /dev/null +++ b/utils/hash/chunk.go @@ -0,0 +1,27 @@ +package hash + +// Chunk 将哈希表按照指定大小分块 +// - m: 待分块的哈希表 +// - size: 每块的大小 +func Chunk[K comparable, V any](m map[K]V, size int) []map[K]V { + if len(m) == 0 { + return nil + } + + var ( + i int + j int + ) + chunks := make([]map[K]V, (len(m)-1)/size+1) + for i = 0; i < len(m); i += size { + chunks[j] = make(map[K]V, size) + for key, value := range m { + if i <= j*size && j*size < i+size { + chunks[j][key] = value + } + } + j++ + } + + return chunks +} diff --git a/utils/hash/drop.go b/utils/hash/drop.go new file mode 100644 index 0000000..050f712 --- /dev/null +++ b/utils/hash/drop.go @@ -0,0 +1,56 @@ +package hash + +// RandomDrop 随机删除哈希表中的指定数量的元素 +// - 该函数会修改原哈希表,如果不想修改原哈希表,请使用 RandomDropCopy +func RandomDrop[K comparable, V any](n int, hash map[K]V) map[K]V { + if n <= 0 || len(hash) <= n { + return hash + } + for k := range hash { + if n <= 0 { + break + } + delete(hash, k) + n-- + } + return hash +} + +// RandomDropCopy 随机删除哈希表中的指定数量的元素 +// - 该函数不会修改原哈希表,如果想修改原哈希表,请使用 RandomDrop +func RandomDropCopy[K comparable, V any](n int, m map[K]V) map[K]V { + if n <= 0 || len(m) <= n { + return map[K]V{} + } + var nm = make(map[K]V, len(m)) + for k, v := range m { + if n <= 0 { + break + } + nm[k] = v + n-- + } + return nm +} + +// DropBy 从哈希表中删除指定的元素 +func DropBy[K comparable, V any](m map[K]V, fn func(key K, value V) bool) map[K]V { + for k, v := range m { + if fn(k, v) { + delete(m, k) + } + } + return m +} + +// DropByCopy 与 DropBy 功能相同,但是该函数不会修改原哈希表 +func DropByCopy[K comparable, V any](m map[K]V, fn func(key K, value V) bool) map[K]V { + var nm = make(map[K]V, len(m)) + for k, v := range m { + if fn(k, v) { + continue + } + nm[k] = v + } + return nm +} diff --git a/utils/hash/each.go b/utils/hash/each.go new file mode 100644 index 0000000..85324b4 --- /dev/null +++ b/utils/hash/each.go @@ -0,0 +1,47 @@ +package hash + +// Each 根据传入的 abort 遍历 m,如果 iterator 返回值与 abort 相同,则停止遍历 +func Each[K comparable, V any](abort bool, m map[K]V, iterator func(i int, key K, item V) bool) { + i := 0 + for k, v := range m { + if iterator(i, k, v) == abort { + break + } + i++ + } +} + +// EachT 与 Each 的功能相同,但是 abort 被默认为 true +func EachT[K comparable, V any](m map[K]V, iterator func(i int, key K, item V) bool) { + Each(true, m, iterator) +} + +// EachF 与 Each 的功能相同,但是 abort 被默认为 false +func EachF[K comparable, V any](m map[K]V, iterator func(i int, key K, item V) bool) { + Each(false, m, iterator) +} + +// EachResult 根据传入的 abort 遍历 m,得到遍历的结果,如果 iterator 返回值中的 bool 值与 abort 相同,则停止遍历,并返回当前已积累的结果 +func EachResult[K comparable, V any, R any](abort bool, m map[K]V, iterator func(i int, key K, item V) (R, bool)) []R { + var result []R + i := 0 + for k, v := range m { + r, ok := iterator(i, k, v) + result = append(result, r) + if ok == abort { + break + } + i++ + } + return result +} + +// EachResultT 与 EachResult 的功能相同,但是 abort 被默认为 true +func EachResultT[K comparable, V any, R any](m map[K]V, iterator func(i int, key K, item V) (R, bool)) []R { + return EachResult(true, m, iterator) +} + +// EachResultF 与 EachResult 的功能相同,但是 abort 被默认为 false +func EachResultF[K comparable, V any, R any](m map[K]V, iterator func(i int, key K, item V) (R, bool)) []R { + return EachResult(false, m, iterator) +} diff --git a/utils/hash/filter.go b/utils/hash/filter.go new file mode 100644 index 0000000..50bed1d --- /dev/null +++ b/utils/hash/filter.go @@ -0,0 +1,56 @@ +package hash + +// Filter 根据特定的表达式过滤哈希表成员 +// - reserve: 是否保留符合条件的成员 +// - m: 待过滤的哈希表 +// - expression: 过滤表达式 +// +// 这个函数的作用是遍历输入哈希表 m,然后根据 expression 函数的返回值来决定是否保留每个元素。具体来说 +// - 如果 expression 返回 true 并且 reserve 也是 true,那么元素会被保留 +// - 如果 expression 返回 false 并且 reserve 是 false,那么元素也会被保留 +// +// 该没有创建新的内存空间或进行元素复制,所以整个操作相当高效。同时,由于 m 和 map 实际上共享底层的数组,因此这个函数会改变传入的 map。如果不希望改变原哈希表,需要在函数调用之前手动复制一份或者使用 FilterCopy 函数。 +func Filter[K comparable, V any](reserve bool, m map[K]V, expression func(key K, value V) bool) map[K]V { + if len(m) == 0 { + return m + } + + for key, value := range m { + if !expression(key, value) { + delete(m, key) + } + } + + return m +} + +// FilterT 与 Filter 的功能相同,但是 reserve 被默认为 true +func FilterT[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V { + return Filter(true, m, expression) +} + +// FilterF 与 Filter 的功能相同,但是 reserve 被默认为 false +func FilterF[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V { + return Filter(false, m, expression) +} + +// FilterCopy 与 Filter 的功能相同,但是不会改变原哈希表,而是返回一个新的哈希表 +func FilterCopy[K comparable, V any](reserve bool, m map[K]V, expression func(key K, value V) bool) map[K]V { + var res = map[K]V{} + for key, value := range m { + if expression(key, value) { + res[key] = value + } + } + return res +} + +// FilterTCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 true +func FilterTCopy[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V { + return FilterCopy(true, m, expression) +} + +// FilterFCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 false +func FilterFCopy[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V { + return FilterCopy(false, m, expression) +} diff --git a/utils/slice/chunk.go b/utils/slice/chunk.go new file mode 100644 index 0000000..311d2f5 --- /dev/null +++ b/utils/slice/chunk.go @@ -0,0 +1,18 @@ +package slice + +// Chunk 返回分块后的切片 +func Chunk[T any](collection []T, size int) [][]T { + if len(collection) == 0 { + return nil + } + + if size < 1 { + panic("size must be greater than 0") + } + + result := make([][]T, 0, (len(collection)+size-1)/size) + for size < len(collection) { + collection, result = collection[size:], append(result, collection[0:size]) + } + return append(result, collection) +} diff --git a/utils/slice/drop.go b/utils/slice/drop.go new file mode 100644 index 0000000..3883121 --- /dev/null +++ b/utils/slice/drop.go @@ -0,0 +1,40 @@ +package slice + +// Drop 从切片中删除指定数量的元素 +// - start: 起始位置 +// - n: 删除的元素数量 +// - slice: 待删除元素的切片 +// +// 关于 start 的取值: +// - 当 start < 0 时,start 将会从右至左计数,即 -1 表示最后一个元素,-2 表示倒数第二个元素,以此类推 +func Drop[V any](start, n int, slice []V) []V { + var s = make([]V, len(slice)) + copy(s, slice) + if start < 0 { + start = len(s) + start - n + 1 + if start < 0 { + start = 0 + } + } + + end := start + n + if end > len(s) { + end = len(s) + } + + return append(s[:start], s[end:]...) +} + +// DropBy 从切片中删除指定的元素 +// - slice: 待删除元素的切片 +func DropBy[V any](slice []V, fn func(index int, value V) bool) []V { + var s = make([]V, len(slice)) + copy(s, slice) + for i := 0; i < len(s); i++ { + if fn(i, s[i]) { + s = append(s[:i], s[i+1:]...) + i-- + } + } + return s +} diff --git a/utils/slice/drop_test.go b/utils/slice/drop_test.go new file mode 100644 index 0000000..33fbe0d --- /dev/null +++ b/utils/slice/drop_test.go @@ -0,0 +1,11 @@ +package slice_test + +import ( + "github.com/kercylan98/minotaur/utils/slice" + "testing" +) + +func TestDrop(t *testing.T) { + s := []int{1, 2, 3, 4, 5} + t.Log(s, slice.Drop(1, 3, s)) +} diff --git a/utils/slice/each.go b/utils/slice/each.go new file mode 100644 index 0000000..9174bf1 --- /dev/null +++ b/utils/slice/each.go @@ -0,0 +1,85 @@ +package slice + +// Each 根据传入的 abort 遍历 slice,如果 iterator 返回值与 abort 相同,则停止遍历 +func Each[V any](abort bool, slice []V, iterator func(index int, item V) bool) { + for i := 0; i < len(slice); i++ { + if iterator(i, slice[i]) == abort { + break + } + } +} + +// EachT 与 Each 的功能相同,但是 abort 被默认为 true +func EachT[V any](slice []V, iterator func(index int, item V) bool) { + Each(true, slice, iterator) +} + +// EachF 与 Each 的功能相同,但是 abort 被默认为 false +func EachF[V any](slice []V, iterator func(index int, item V) bool) { + Each(false, slice, iterator) +} + +// EachReverse 根据传入的 abort 从后往前遍历 slice,如果 iterator 返回值与 abort 相同,则停止遍历 +func EachReverse[V any](abort bool, slice []V, iterator func(index int, item V) bool) { + for i := len(slice) - 1; i >= 0; i-- { + if iterator(i, slice[i]) == abort { + break + } + } +} + +// EachReverseT 与 EachReverse 的功能相同,但是 abort 被默认为 true +func EachReverseT[V any](slice []V, iterator func(index int, item V) bool) { + EachReverse(true, slice, iterator) +} + +// EachReverseF 与 EachReverse 的功能相同,但是 abort 被默认为 false +func EachReverseF[V any](slice []V, iterator func(index int, item V) bool) { + EachReverse(false, slice, iterator) +} + +// EachResult 根据传入的 abort 遍历 slice,得到遍历的结果,如果 iterator 返回值中的 bool 值与 abort 相同,则停止遍历,并返回当前已积累的结果 +func EachResult[V any, R any](abort bool, slice []V, iterator func(index int, item V) (R, bool)) []R { + var result []R + for i := 0; i < len(slice); i++ { + r, ok := iterator(i, slice[i]) + result = append(result, r) + if ok == abort { + break + } + } + return result +} + +// EachResultT 与 EachResult 的功能相同,但是 abort 被默认为 true +func EachResultT[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R { + return EachResult(true, slice, iterator) +} + +// EachResultF 与 EachResult 的功能相同,但是 abort 被默认为 false +func EachResultF[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R { + return EachResult(false, slice, iterator) +} + +// EachResultReverse 根据传入的 abort 从后往前遍历 slice,得到遍历的结果,如果 iterator 返回值中的 bool 值与 abort 相同,则停止遍历,并返回当前已积累的结果 +func EachResultReverse[V any, R any](abort bool, slice []V, iterator func(index int, item V) (R, bool)) []R { + var result []R + for i := len(slice) - 1; i >= 0; i-- { + r, ok := iterator(i, slice[i]) + result = append(result, r) + if ok == abort { + break + } + } + return result +} + +// EachResultReverseT 与 EachResultReverse 的功能相同,但是 abort 被默认为 true +func EachResultReverseT[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R { + return EachResultReverse(true, slice, iterator) +} + +// EachResultReverseF 与 EachResultReverse 的功能相同,但是 abort 被默认为 false +func EachResultReverseF[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R { + return EachResultReverse(false, slice, iterator) +} diff --git a/utils/slice/fill.go b/utils/slice/fill.go new file mode 100644 index 0000000..a345315 --- /dev/null +++ b/utils/slice/fill.go @@ -0,0 +1,58 @@ +package slice + +// FillBy 用指定的值填充切片 +// - slice: 待填充的切片 +func FillBy[V any](slice []V, fn func(index int, value V) V) []V { + for i, v := range slice { + slice[i] = fn(i, v) + } + return slice +} + +// FillByCopy 与 FillBy 的功能相同,但是不会改变原切片,而是返回一个新的切片 +func FillByCopy[V any](slice []V, fn func(index int, value V) V) []V { + var s = make([]V, len(slice)) + copy(s, slice) + return FillBy(s, fn) +} + +// FillUntil 用指定的值填充切片,如果 fn 返回的 bool 值与 abort 相同,则停止填充 +// - abort: 填充中止条件 +// - slice: 待填充的切片 +func FillUntil[V any](abort bool, slice []V, fn func(index int, value V) (V, bool)) []V { + for i, v := range slice { + if value, b := fn(i, v); b == abort { + break + } else { + slice[i] = value + } + } + return slice +} + +// FillUntilCopy 与 FillUntil 的功能相同,但不会改变原切片,而是返回一个新的切片 +func FillUntilCopy[V any](abort bool, slice []V, fn func(index int, value V) (V, bool)) []V { + var s = make([]V, len(slice)) + copy(s, slice) + return FillUntil(abort, s, fn) +} + +// FillUntilT 是 FillUntil 的简化版本,其中 abort 参数为 true +func FillUntilT[V any](slice []V, fn func(index int, value V) (V, bool)) []V { + return FillUntil(true, slice, fn) +} + +// FillUntilF 是 FillUntil 的简化版本,其中 abort 参数为 false +func FillUntilF[V any](slice []V, fn func(index int, value V) (V, bool)) []V { + return FillUntil(false, slice, fn) +} + +// FillUntilTCopy 是 FillUntilCopy 的简化版本,其中 abort 参数为 true +func FillUntilTCopy[V any](slice []V, fn func(index int, value V) (V, bool)) []V { + return FillUntilCopy(true, slice, fn) +} + +// FillUntilFCopy 是 FillUntilCopy 的简化版本,其中 abort 参数为 false +func FillUntilFCopy[V any](slice []V, fn func(index int, value V) (V, bool)) []V { + return FillUntilCopy(false, slice, fn) +} diff --git a/utils/slice/filter.go b/utils/slice/filter.go index bae96ca..2a8a806 100644 --- a/utils/slice/filter.go +++ b/utils/slice/filter.go @@ -53,12 +53,12 @@ func FilterCopy[V any](reserve bool, slice []V, expression func(index int, item return Filter(reserve, s, expression) } -// FilterCopyT 与 FilterCopy 的功能相同,但是 reserve 被默认为 true -func FilterCopyT[V any](slice []V, expression func(index int, item V) bool) []V { +// FilterTCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 true +func FilterTCopy[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 { +// FilterFCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 false +func FilterFCopy[V any](slice []V, expression func(index int, item V) bool) []V { return FilterCopy(false, slice, expression) } diff --git a/utils/slice/group.go b/utils/slice/group.go new file mode 100644 index 0000000..503333f --- /dev/null +++ b/utils/slice/group.go @@ -0,0 +1,17 @@ +package slice + +// GroupBy 返回分组后的切片 +func GroupBy[T any, K comparable](collection []T, fn func(T) K) map[K][]T { + if len(collection) == 0 { + return nil + } + + result := make(map[K][]T, len(collection)) + + for _, item := range collection { + key := fn(item) + result[key] = append(result[key], item) + } + + return result +} diff --git a/utils/slice/shuffle.go b/utils/slice/shuffle.go new file mode 100644 index 0000000..bb99be8 --- /dev/null +++ b/utils/slice/shuffle.go @@ -0,0 +1,30 @@ +package slice + +import "math/rand" + +// Shuffle 随机打乱切片 +// - 该函数会改变传入的切片,如果不希望改变原切片,需要在函数调用之前手动复制一份或者使用 ShuffleCopy 函数 +func Shuffle[T any](collection []T) []T { + rand.Shuffle(len(collection), func(i, j int) { + collection[i], collection[j] = collection[j], collection[i] + }) + + return collection +} + +// ShuffleCopy 返回随机打乱后的切片 +// - 该函数不会改变原切片 +func ShuffleCopy[T any](collection []T) []T { + if len(collection) == 0 { + return nil + } + + result := make([]T, len(collection)) + perm := rand.Perm(len(collection)) + + for i, randIndex := range perm { + result[i] = collection[randIndex] + } + + return result +} diff --git a/utils/slice/slice.go b/utils/slice/slice.go index 23c386f..7948a1a 100644 --- a/utils/slice/slice.go +++ b/utils/slice/slice.go @@ -1,7 +1,6 @@ package slice import ( - "math/rand" "reflect" ) @@ -111,14 +110,6 @@ func Reverse[V any](slice []V) { } } -// Shuffle 随机打乱数组 -func Shuffle[V any](slice []V) { - for i := 0; i < len(slice); i++ { - j := rand.Intn(len(slice)) - slice[i], slice[j] = slice[j], slice[i] - } -} - // Distinct 去重 func Distinct[V any](slice []V) []V { var result []V diff --git a/utils/slice/unique.go b/utils/slice/unique.go new file mode 100644 index 0000000..cca9197 --- /dev/null +++ b/utils/slice/unique.go @@ -0,0 +1,44 @@ +package slice + +// Unique 返回去重后的切片 +func Unique[T comparable](collection []T) []T { + if len(collection) == 0 { + return nil + } + + result := make([]T, 0, len(collection)) + seen := make(map[T]struct{}, len(collection)) + + for _, item := range collection { + if _, ok := seen[item]; ok { + continue + } + + seen[item] = struct{}{} + result = append(result, item) + } + + return result +} + +// UniqueBy 返回去重后的切片 +func UniqueBy[T any](collection []T, fn func(T) any) []T { + if len(collection) == 0 { + return nil + } + + result := make([]T, 0, len(collection)) + seen := make(map[any]struct{}, len(collection)) + + for _, item := range collection { + key := fn(item) + if _, ok := seen[key]; ok { + continue + } + + seen[key] = struct{}{} + result = append(result, item) + } + + return result +}