All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
feat(utils): 添加切片元素计数功能 新增CountSliceElements函数用于统计切片中各元素的出现次数, 返回map[元素]出现次数的映射关系,支持任意可比较类型的切片元素。 fix(config): 调整地图配置模型默认值设置 修改MapPit结构体中的MinLevel和MaxLevel字段设置, 将数据库约束改为非空并设置默认值为1,确保等级范围配置的有效性。 ref
102 lines
2.9 KiB
Go
102 lines
2.9 KiB
Go
package utils
|
||
|
||
import "github.com/gogf/gf/v2/util/grand"
|
||
|
||
// ToMap converts a slice to a map with the keyFunc determining what the key of a value should be.
|
||
// Will override any double values.
|
||
func ToMap[T any, K comparable](slice []T, keyFunc func(T) K) map[K]T {
|
||
m := make(map[K]T, len(slice))
|
||
for _, v := range slice {
|
||
m[keyFunc(v)] = v
|
||
}
|
||
return m
|
||
}
|
||
|
||
// ToSliceMap converts a slice to a map with the keyFunc determining what the key of a value should be.
|
||
// Will append to the slice if the key already exists.
|
||
func ToSliceMap[T any, K comparable](slice []T, keyFunc func(T) K) map[K][]T {
|
||
m := make(map[K][]T, len(slice))
|
||
for _, v := range slice {
|
||
key := keyFunc(v)
|
||
m[key] = append(m[key], v)
|
||
}
|
||
return m
|
||
}
|
||
|
||
// 定义泛型约束:只允许数值类型(整数、浮点数等)
|
||
type Number interface {
|
||
int | int8 | int16 | int32 | int64 |
|
||
uint | uint8 | uint16 | uint32 | uint64 |
|
||
float32 | float64
|
||
}
|
||
|
||
// Max 泛型函数:接收两个同类型的 Number 参数,返回最大值
|
||
func Max[T Number](a, b T) T {
|
||
if a > b {
|
||
return a
|
||
}
|
||
return b
|
||
}
|
||
|
||
// Max 泛型函数:接收两个同类型的 Number 参数,返回最大值
|
||
func Min[T Number](a, b T) T {
|
||
if a < b {
|
||
return a
|
||
}
|
||
return b
|
||
}
|
||
|
||
// RandomSlice 泛型函数:从任意类型的切片中随机选取n个不重复元素
|
||
// T: 泛型类型参数,any表示兼容任意类型
|
||
// slice: 源切片(任意类型)
|
||
// n: 要选取的元素数量
|
||
// 返回值: 与源切片同类型的新切片,包含随机选取的n个元素
|
||
func RandomSlice[T any](slice []T, n int) []T {
|
||
// 边界条件1:n≤0 或 源切片为空 → 返回空切片
|
||
if n <= 0 || len(slice) == 0 {
|
||
return []T{}
|
||
}
|
||
|
||
// 边界条件2:n≥源切片长度 → 返回源切片的拷贝(避免修改原切片)
|
||
if n >= len(slice) {
|
||
copySlice := make([]T, len(slice))
|
||
copy(copySlice, slice)
|
||
return copySlice
|
||
}
|
||
|
||
// 步骤1:生成源切片的索引切片
|
||
indices := make([]int, len(slice))
|
||
for i := range indices {
|
||
indices[i] = i
|
||
}
|
||
|
||
// 步骤2:Fisher-Yates洗牌算法打乱索引(保证随机性均匀)
|
||
for i := len(indices) - 1; i > 0; i-- {
|
||
j := grand.Intn(i + 1) // 生成0~i的随机数
|
||
indices[i], indices[j] = indices[j], indices[i]
|
||
}
|
||
|
||
// 步骤3:取前n个打乱后的索引,映射为源切片的元素
|
||
result := make([]T, n)
|
||
for i := 0; i < n; i++ {
|
||
result[i] = slice[indices[i]]
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
// T: 切片元素类型(必须是可比较类型,满足map键的要求)
|
||
// 返回值:map[T]int - 键为切片元素,值为对应出现次数
|
||
func CountSliceElements[T comparable](slice []T) map[T]int {
|
||
// 初始化map,预设容量为切片长度(优化性能)
|
||
countMap := make(map[T]int, len(slice))
|
||
|
||
// 遍历切片,统计每个元素的出现次数
|
||
for _, v := range slice {
|
||
// 若元素已存在,值+1;不存在则自动初始化为0后+1
|
||
countMap[v]++
|
||
}
|
||
|
||
return countMap
|
||
}
|