refactor: 移除 slice 包和 hash 包,新增 listings、mappings 包存放数组、切片、映射等数据结构,原 slice、hash 包中的工具函数迁移至 collection 包,与 sher 包合并并移除 sher 包。完善 collection 包测试用例
This commit is contained in:
parent
3408c212d0
commit
66d903474d
|
@ -2,8 +2,8 @@ package activity
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/times"
|
||||
"reflect"
|
||||
"time"
|
||||
|
@ -94,7 +94,7 @@ func regController[Type, ID generic.Basic, Data any, EntityID generic.Basic, Ent
|
|||
// 实体数据加载器
|
||||
activityEntityDataLoader = append(activityEntityDataLoader, func(handler func(activityType any, activityId any, entityId any, data any)) {
|
||||
controller.mutex.RLock()
|
||||
entities := hash.Copy(controller.entityData[activityId])
|
||||
entities := collection.CloneMap(controller.entityData[activityId])
|
||||
controller.mutex.RUnlock()
|
||||
for entityId, data := range entities {
|
||||
handler(controller.t, activityId, entityId, data)
|
||||
|
|
|
@ -2,9 +2,10 @@ package activity
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
listings2 "github.com/kercylan98/minotaur/utils/collection/listings"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"github.com/kercylan98/minotaur/utils/timer"
|
||||
"github.com/kercylan98/minotaur/utils/times"
|
||||
"reflect"
|
||||
|
@ -21,28 +22,28 @@ type (
|
|||
)
|
||||
|
||||
var (
|
||||
upcomingEventHandlers map[any]*slice.Priority[func(activityId any)] // 即将开始的活动事件处理器
|
||||
startedEventHandlers map[any]*slice.Priority[func(activityId any)] // 活动开始事件处理器
|
||||
endedEventHandlers map[any]*slice.Priority[func(activityId any)] // 活动结束事件处理器
|
||||
extShowStartedEventHandlers map[any]*slice.Priority[func(activityId any)] // 活动结束后延长展示开始事件处理器
|
||||
extShowEndedEventHandlers map[any]*slice.Priority[func(activityId any)] // 活动结束后延长展示结束事件处理器
|
||||
newDayEventHandlers map[any]*slice.Priority[func(activityId any)] // 新的一天事件处理器
|
||||
upcomingEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 即将开始的活动事件处理器
|
||||
startedEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 活动开始事件处理器
|
||||
endedEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 活动结束事件处理器
|
||||
extShowStartedEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 活动结束后延长展示开始事件处理器
|
||||
extShowEndedEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 活动结束后延长展示结束事件处理器
|
||||
newDayEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 新的一天事件处理器
|
||||
)
|
||||
|
||||
func init() {
|
||||
upcomingEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
||||
startedEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
||||
endedEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
||||
extShowStartedEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
||||
extShowEndedEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
||||
newDayEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
||||
upcomingEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||
startedEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||
endedEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||
extShowStartedEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||
extShowEndedEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||
newDayEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||
}
|
||||
|
||||
// RegUpcomingEvent 注册即将开始的活动事件处理器
|
||||
func RegUpcomingEvent[Type, ID generic.Basic](activityType Type, handler UpcomingEventHandler[ID], priority ...int) {
|
||||
handlers, exist := upcomingEventHandlers[activityType]
|
||||
if !exist {
|
||||
handlers = slice.NewPriority[func(activityId any)]()
|
||||
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||
upcomingEventHandlers[activityType] = handlers
|
||||
}
|
||||
handlers.Append(func(activityId any) {
|
||||
|
@ -50,7 +51,7 @@ func RegUpcomingEvent[Type, ID generic.Basic](activityType Type, handler Upcomin
|
|||
return
|
||||
}
|
||||
handler(activityId.(ID))
|
||||
}, slice.GetValue(priority, 0))
|
||||
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
// OnUpcomingEvent 即将开始的活动事件
|
||||
|
@ -75,7 +76,7 @@ func OnUpcomingEvent[Type, ID generic.Basic](activity *Activity[Type, ID]) {
|
|||
func RegStartedEvent[Type, ID generic.Basic](activityType Type, handler StartedEventHandler[ID], priority ...int) {
|
||||
handlers, exist := startedEventHandlers[activityType]
|
||||
if !exist {
|
||||
handlers = slice.NewPriority[func(activityId any)]()
|
||||
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||
startedEventHandlers[activityType] = handlers
|
||||
}
|
||||
handlers.Append(func(activityId any) {
|
||||
|
@ -83,7 +84,7 @@ func RegStartedEvent[Type, ID generic.Basic](activityType Type, handler StartedE
|
|||
return
|
||||
}
|
||||
handler(activityId.(ID))
|
||||
}, slice.GetValue(priority, 0))
|
||||
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
// OnStartedEvent 活动开始事件
|
||||
|
@ -116,7 +117,7 @@ func OnStartedEvent[Type, ID generic.Basic](activity *Activity[Type, ID]) {
|
|||
func RegEndedEvent[Type, ID generic.Basic](activityType Type, handler EndedEventHandler[ID], priority ...int) {
|
||||
handlers, exist := endedEventHandlers[activityType]
|
||||
if !exist {
|
||||
handlers = slice.NewPriority[func(activityId any)]()
|
||||
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||
endedEventHandlers[activityType] = handlers
|
||||
}
|
||||
handlers.Append(func(activityId any) {
|
||||
|
@ -124,7 +125,7 @@ func RegEndedEvent[Type, ID generic.Basic](activityType Type, handler EndedEvent
|
|||
return
|
||||
}
|
||||
handler(activityId.(ID))
|
||||
}, slice.GetValue(priority, 0))
|
||||
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
// OnEndedEvent 活动结束事件
|
||||
|
@ -149,7 +150,7 @@ func OnEndedEvent[Type, ID generic.Basic](activity *Activity[Type, ID]) {
|
|||
func RegExtendedShowStartedEvent[Type, ID generic.Basic](activityType Type, handler ExtendedShowStartedEventHandler[ID], priority ...int) {
|
||||
handlers, exist := extShowStartedEventHandlers[activityType]
|
||||
if !exist {
|
||||
handlers = slice.NewPriority[func(activityId any)]()
|
||||
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||
extShowStartedEventHandlers[activityType] = handlers
|
||||
}
|
||||
handlers.Append(func(activityId any) {
|
||||
|
@ -157,7 +158,7 @@ func RegExtendedShowStartedEvent[Type, ID generic.Basic](activityType Type, hand
|
|||
return
|
||||
}
|
||||
handler(activityId.(ID))
|
||||
}, slice.GetValue(priority, 0))
|
||||
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
// OnExtendedShowStartedEvent 活动结束后延长展示开始事件
|
||||
|
@ -182,7 +183,7 @@ func OnExtendedShowStartedEvent[Type, ID generic.Basic](activity *Activity[Type,
|
|||
func RegExtendedShowEndedEvent[Type, ID generic.Basic](activityType Type, handler ExtendedShowEndedEventHandler[ID], priority ...int) {
|
||||
handlers, exist := extShowEndedEventHandlers[activityType]
|
||||
if !exist {
|
||||
handlers = slice.NewPriority[func(activityId any)]()
|
||||
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||
extShowEndedEventHandlers[activityType] = handlers
|
||||
}
|
||||
handlers.Append(func(activityId any) {
|
||||
|
@ -190,7 +191,7 @@ func RegExtendedShowEndedEvent[Type, ID generic.Basic](activityType Type, handle
|
|||
return
|
||||
}
|
||||
handler(activityId.(ID))
|
||||
}, slice.GetValue(priority, 0))
|
||||
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
// OnExtendedShowEndedEvent 活动结束后延长展示结束事件
|
||||
|
@ -215,7 +216,7 @@ func OnExtendedShowEndedEvent[Type, ID generic.Basic](activity *Activity[Type, I
|
|||
func RegNewDayEvent[Type, ID generic.Basic](activityType Type, handler NewDayEventHandler[ID], priority ...int) {
|
||||
handlers, exist := newDayEventHandlers[activityType]
|
||||
if !exist {
|
||||
handlers = slice.NewPriority[func(activityId any)]()
|
||||
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||
newDayEventHandlers[activityType] = handlers
|
||||
}
|
||||
handlers.Append(func(activityId any) {
|
||||
|
@ -223,7 +224,7 @@ func RegNewDayEvent[Type, ID generic.Basic](activityType Type, handler NewDayEve
|
|||
return
|
||||
}
|
||||
handler(activityId.(ID))
|
||||
}, slice.GetValue(priority, 0))
|
||||
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
// OnNewDayEvent 新的一天事件
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package space
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -201,7 +200,7 @@ func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetNotEmptySeat() []in
|
|||
// GetEmptySeat 获取空座位
|
||||
// - 空座位需要在有对象离开座位后才可能出现
|
||||
func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEmptySeat() []int {
|
||||
return slice.Copy(rc.vacancy)
|
||||
return collection.CloneSlice(rc.vacancy)
|
||||
}
|
||||
|
||||
// HasSeat 判断是否有座位
|
||||
|
@ -291,7 +290,7 @@ func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetRoom() Room {
|
|||
func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntities() map[EntityID]Entity {
|
||||
rc.entitiesRWMutex.RLock()
|
||||
defer rc.entitiesRWMutex.RUnlock()
|
||||
return hash.Copy(rc.entities)
|
||||
return collection.CloneMap(rc.entities)
|
||||
}
|
||||
|
||||
// HasEntity 判断是否有实体
|
||||
|
@ -321,7 +320,7 @@ func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntityExist(id Enti
|
|||
func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntityIDs() []EntityID {
|
||||
rc.entitiesRWMutex.RLock()
|
||||
defer rc.entitiesRWMutex.RUnlock()
|
||||
return hash.KeyToSlice(rc.entities)
|
||||
return collection.ConvertMapKeysToSlice(rc.entities)
|
||||
}
|
||||
|
||||
// GetEntityCount 获取实体数量
|
||||
|
@ -454,7 +453,7 @@ func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetRoomID() RoomID {
|
|||
// Broadcast 广播,该函数会将所有房间中满足 conditions 的对象传入 handler 中进行处理
|
||||
func (rc *RoomController[EntityID, RoomID, Entity, Room]) Broadcast(handler func(Entity), conditions ...func(Entity) bool) {
|
||||
rc.entitiesRWMutex.RLock()
|
||||
entities := hash.Copy(rc.entities)
|
||||
entities := collection.CloneMap(rc.entities)
|
||||
rc.entitiesRWMutex.RUnlock()
|
||||
for _, entity := range entities {
|
||||
for _, condition := range conditions {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package space
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -54,7 +54,7 @@ func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRoom(id RoomID) *RoomC
|
|||
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRooms() map[RoomID]*RoomController[EntityID, RoomID, Entity, Room] {
|
||||
rm.roomsRWMutex.RLock()
|
||||
defer rm.roomsRWMutex.RUnlock()
|
||||
return hash.Copy(rm.rooms)
|
||||
return collection.CloneMap(rm.rooms)
|
||||
}
|
||||
|
||||
// GetRoomCount 获取房间管理器接管的房间数量
|
||||
|
@ -68,13 +68,13 @@ func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomCount() int {
|
|||
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomIDs() []RoomID {
|
||||
rm.roomsRWMutex.RLock()
|
||||
defer rm.roomsRWMutex.RUnlock()
|
||||
return hash.KeyToSlice(rm.rooms)
|
||||
return collection.ConvertMapKeysToSlice(rm.rooms)
|
||||
}
|
||||
|
||||
// HasEntity 判断特定对象是否在任一房间中,当对象不在任一房间中时将返回 false
|
||||
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) HasEntity(entityId EntityID) bool {
|
||||
rm.roomsRWMutex.RLock()
|
||||
rooms := hash.Copy(rm.rooms)
|
||||
rooms := collection.CloneMap(rm.rooms)
|
||||
rm.roomsRWMutex.RUnlock()
|
||||
for _, room := range rooms {
|
||||
if room.HasEntity(entityId) {
|
||||
|
@ -88,7 +88,7 @@ func (rm *RoomManager[EntityID, RoomID, Entity, Room]) HasEntity(entityId Entity
|
|||
// - 由于一个对象可能在多个房间中,因此返回值为 map 类型
|
||||
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetEntityRooms(entityId EntityID) map[RoomID]*RoomController[EntityID, RoomID, Entity, Room] {
|
||||
rm.roomsRWMutex.RLock()
|
||||
rooms := hash.Copy(rm.rooms)
|
||||
rooms := collection.CloneMap(rm.rooms)
|
||||
rm.roomsRWMutex.RUnlock()
|
||||
var result = make(map[RoomID]*RoomController[EntityID, RoomID, Entity, Room])
|
||||
for id, room := range rooms {
|
||||
|
@ -102,7 +102,7 @@ func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetEntityRooms(entityId E
|
|||
// Broadcast 向所有房间对象广播消息,该方法将会遍历所有房间控制器并调用 RoomController.Broadcast 方法
|
||||
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) Broadcast(handler func(Entity), conditions ...func(Entity) bool) {
|
||||
rm.roomsRWMutex.RLock()
|
||||
rooms := hash.Copy(rm.rooms)
|
||||
rooms := collection.CloneMap(rm.rooms)
|
||||
rm.roomsRWMutex.RUnlock()
|
||||
for _, room := range rooms {
|
||||
room.Broadcast(handler, conditions...)
|
||||
|
|
|
@ -37,7 +37,7 @@ func TestCond(t *testing.T) {
|
|||
|
||||
player := &Player{
|
||||
tasks: map[string][]*Task{
|
||||
task.Type: []*Task{task},
|
||||
task.Type: {task},
|
||||
},
|
||||
}
|
||||
OnRefreshTaskCounterEvent(task.Type, player, 1)
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"github.com/kercylan98/minotaur/planner/pce"
|
||||
"github.com/kercylan98/minotaur/planner/pce/cs"
|
||||
"github.com/kercylan98/minotaur/planner/pce/tmpls"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/file"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tealeg/xlsx"
|
||||
|
@ -64,7 +64,7 @@ func init() {
|
|||
var exporter = pce.NewExporter()
|
||||
loader := pce.NewLoader(pce.GetFields())
|
||||
|
||||
excludes := hash.ToMapBool(str.SplitTrimSpace(exclude, ","))
|
||||
excludes := collection.ConvertSliceToBoolMap(str.SplitTrimSpace(exclude, ","))
|
||||
for _, xlsxFile := range xlsxFiles {
|
||||
xf, err := xlsx.OpenFile(xlsxFile)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
"github.com/kercylan98/minotaur/planner/pce"
|
||||
"github.com/kercylan98/minotaur/planner/pce/cs"
|
||||
"github.com/kercylan98/minotaur/planner/pce/tmpls"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/file"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tealeg/xlsx"
|
||||
|
@ -61,7 +61,7 @@ func init() {
|
|||
var exporter = pce.NewExporter()
|
||||
loader := pce.NewLoader(pce.GetFields())
|
||||
|
||||
excludes := hash.ToMapBool(str.SplitTrimSpace(exclude, ","))
|
||||
excludes := collection.ConvertSliceToBoolMap(str.SplitTrimSpace(exclude, ","))
|
||||
for _, xlsxFile := range xlsxFiles {
|
||||
xf, err := xlsx.OpenFile(xlsxFile)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
"github.com/kercylan98/minotaur/planner/pce"
|
||||
"github.com/kercylan98/minotaur/planner/pce/cs"
|
||||
"github.com/kercylan98/minotaur/planner/pce/tmpls"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/file"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"github.com/tealeg/xlsx"
|
||||
"os"
|
||||
|
@ -61,7 +61,7 @@ func TestExecute(t *testing.T) {
|
|||
var exporter = pce.NewExporter()
|
||||
loader := pce.NewLoader(pce.GetFields())
|
||||
|
||||
excludes := hash.ToMapBool(str.SplitTrimSpace(exclude, ","))
|
||||
excludes := collection.ConvertSliceToBoolMap(str.SplitTrimSpace(exclude, ","))
|
||||
for _, xlsxFile := range xlsxFiles {
|
||||
xf, err := xlsx.OpenFile(xlsxFile)
|
||||
if err != nil {
|
||||
|
|
|
@ -2,7 +2,7 @@ package pce
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"strings"
|
||||
)
|
||||
|
@ -96,7 +96,7 @@ func (slf *TmplField) handleSlice(fieldName, fieldType string, fields map[string
|
|||
}
|
||||
slf.slice = true
|
||||
t := strings.TrimPrefix(fieldType, "[]")
|
||||
if hash.Exist(fields, t) {
|
||||
if collection.FindInMapKey(fields, t) {
|
||||
slf.Struct = nil
|
||||
slf.Type = t
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package pce
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
)
|
||||
|
||||
// TmplStruct 模板结构
|
||||
|
@ -19,7 +19,7 @@ func (slf *TmplStruct) addField(parent, name, desc, fieldType string, fields map
|
|||
Desc: desc,
|
||||
Type: fieldType,
|
||||
}
|
||||
if !hash.Exist(fields, fieldType) {
|
||||
if !collection.FindInMapKey(fields, fieldType) {
|
||||
field.setStruct(parent, name, desc, fieldType, fields)
|
||||
} else {
|
||||
field.Type = GetFieldGolangType(fields[fieldType])
|
||||
|
|
|
@ -45,5 +45,5 @@ func TestNewBot(t *testing.T) {
|
|||
bot.SendPacket([]byte("hello"))
|
||||
})
|
||||
|
||||
srv.Run(":9600")
|
||||
_ = srv.Run(":9600")
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
"fmt"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/kercylan98/minotaur/server/writeloop"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/random"
|
||||
"github.com/kercylan98/minotaur/utils/timer"
|
||||
|
@ -203,7 +203,7 @@ func (slf *Conn) GetData(key any) any {
|
|||
|
||||
// ViewData 查看只读的连接数据
|
||||
func (slf *Conn) ViewData() map[any]any {
|
||||
return hash.Copy(slf.data)
|
||||
return collection.CloneMap(slf.data)
|
||||
}
|
||||
|
||||
// SetMessageData 设置消息数据,该数据将在消息处理完成后释放
|
||||
|
|
107
server/event.go
107
server/event.go
|
@ -2,9 +2,10 @@ package server
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
listings2 "github.com/kercylan98/minotaur/utils/collection/listings"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/runtimes"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -42,51 +43,51 @@ type (
|
|||
func newEvent(srv *Server) *event {
|
||||
return &event{
|
||||
Server: srv,
|
||||
startBeforeEventHandlers: slice.NewPriority[StartBeforeEventHandler](),
|
||||
startFinishEventHandlers: slice.NewPriority[StartFinishEventHandler](),
|
||||
stopEventHandlers: slice.NewPriority[StopEventHandler](),
|
||||
connectionReceivePacketEventHandlers: slice.NewPriority[ConnectionReceivePacketEventHandler](),
|
||||
connectionOpenedEventHandlers: slice.NewPriority[ConnectionOpenedEventHandler](),
|
||||
connectionClosedEventHandlers: slice.NewPriority[ConnectionClosedEventHandler](),
|
||||
messageErrorEventHandlers: slice.NewPriority[MessageErrorEventHandler](),
|
||||
messageLowExecEventHandlers: slice.NewPriority[MessageLowExecEventHandler](),
|
||||
connectionOpenedAfterEventHandlers: slice.NewPriority[ConnectionOpenedAfterEventHandler](),
|
||||
connectionWritePacketBeforeHandlers: slice.NewPriority[ConnectionWritePacketBeforeEventHandler](),
|
||||
shuntChannelCreatedEventHandlers: slice.NewPriority[ShuntChannelCreatedEventHandler](),
|
||||
shuntChannelClosedEventHandlers: slice.NewPriority[ShuntChannelClosedEventHandler](),
|
||||
connectionPacketPreprocessEventHandlers: slice.NewPriority[ConnectionPacketPreprocessEventHandler](),
|
||||
messageExecBeforeEventHandlers: slice.NewPriority[MessageExecBeforeEventHandler](),
|
||||
messageReadyEventHandlers: slice.NewPriority[MessageReadyEventHandler](),
|
||||
deadlockDetectEventHandlers: slice.NewPriority[OnDeadlockDetectEventHandler](),
|
||||
startBeforeEventHandlers: listings2.NewPrioritySlice[StartBeforeEventHandler](),
|
||||
startFinishEventHandlers: listings2.NewPrioritySlice[StartFinishEventHandler](),
|
||||
stopEventHandlers: listings2.NewPrioritySlice[StopEventHandler](),
|
||||
connectionReceivePacketEventHandlers: listings2.NewPrioritySlice[ConnectionReceivePacketEventHandler](),
|
||||
connectionOpenedEventHandlers: listings2.NewPrioritySlice[ConnectionOpenedEventHandler](),
|
||||
connectionClosedEventHandlers: listings2.NewPrioritySlice[ConnectionClosedEventHandler](),
|
||||
messageErrorEventHandlers: listings2.NewPrioritySlice[MessageErrorEventHandler](),
|
||||
messageLowExecEventHandlers: listings2.NewPrioritySlice[MessageLowExecEventHandler](),
|
||||
connectionOpenedAfterEventHandlers: listings2.NewPrioritySlice[ConnectionOpenedAfterEventHandler](),
|
||||
connectionWritePacketBeforeHandlers: listings2.NewPrioritySlice[ConnectionWritePacketBeforeEventHandler](),
|
||||
shuntChannelCreatedEventHandlers: listings2.NewPrioritySlice[ShuntChannelCreatedEventHandler](),
|
||||
shuntChannelClosedEventHandlers: listings2.NewPrioritySlice[ShuntChannelClosedEventHandler](),
|
||||
connectionPacketPreprocessEventHandlers: listings2.NewPrioritySlice[ConnectionPacketPreprocessEventHandler](),
|
||||
messageExecBeforeEventHandlers: listings2.NewPrioritySlice[MessageExecBeforeEventHandler](),
|
||||
messageReadyEventHandlers: listings2.NewPrioritySlice[MessageReadyEventHandler](),
|
||||
deadlockDetectEventHandlers: listings2.NewPrioritySlice[OnDeadlockDetectEventHandler](),
|
||||
}
|
||||
}
|
||||
|
||||
type event struct {
|
||||
*Server
|
||||
startBeforeEventHandlers *slice.Priority[StartBeforeEventHandler]
|
||||
startFinishEventHandlers *slice.Priority[StartFinishEventHandler]
|
||||
stopEventHandlers *slice.Priority[StopEventHandler]
|
||||
connectionReceivePacketEventHandlers *slice.Priority[ConnectionReceivePacketEventHandler]
|
||||
connectionOpenedEventHandlers *slice.Priority[ConnectionOpenedEventHandler]
|
||||
connectionClosedEventHandlers *slice.Priority[ConnectionClosedEventHandler]
|
||||
messageErrorEventHandlers *slice.Priority[MessageErrorEventHandler]
|
||||
messageLowExecEventHandlers *slice.Priority[MessageLowExecEventHandler]
|
||||
connectionOpenedAfterEventHandlers *slice.Priority[ConnectionOpenedAfterEventHandler]
|
||||
connectionWritePacketBeforeHandlers *slice.Priority[ConnectionWritePacketBeforeEventHandler]
|
||||
shuntChannelCreatedEventHandlers *slice.Priority[ShuntChannelCreatedEventHandler]
|
||||
shuntChannelClosedEventHandlers *slice.Priority[ShuntChannelClosedEventHandler]
|
||||
connectionPacketPreprocessEventHandlers *slice.Priority[ConnectionPacketPreprocessEventHandler]
|
||||
messageExecBeforeEventHandlers *slice.Priority[MessageExecBeforeEventHandler]
|
||||
messageReadyEventHandlers *slice.Priority[MessageReadyEventHandler]
|
||||
deadlockDetectEventHandlers *slice.Priority[OnDeadlockDetectEventHandler]
|
||||
startBeforeEventHandlers *listings2.PrioritySlice[StartBeforeEventHandler]
|
||||
startFinishEventHandlers *listings2.PrioritySlice[StartFinishEventHandler]
|
||||
stopEventHandlers *listings2.PrioritySlice[StopEventHandler]
|
||||
connectionReceivePacketEventHandlers *listings2.PrioritySlice[ConnectionReceivePacketEventHandler]
|
||||
connectionOpenedEventHandlers *listings2.PrioritySlice[ConnectionOpenedEventHandler]
|
||||
connectionClosedEventHandlers *listings2.PrioritySlice[ConnectionClosedEventHandler]
|
||||
messageErrorEventHandlers *listings2.PrioritySlice[MessageErrorEventHandler]
|
||||
messageLowExecEventHandlers *listings2.PrioritySlice[MessageLowExecEventHandler]
|
||||
connectionOpenedAfterEventHandlers *listings2.PrioritySlice[ConnectionOpenedAfterEventHandler]
|
||||
connectionWritePacketBeforeHandlers *listings2.PrioritySlice[ConnectionWritePacketBeforeEventHandler]
|
||||
shuntChannelCreatedEventHandlers *listings2.PrioritySlice[ShuntChannelCreatedEventHandler]
|
||||
shuntChannelClosedEventHandlers *listings2.PrioritySlice[ShuntChannelClosedEventHandler]
|
||||
connectionPacketPreprocessEventHandlers *listings2.PrioritySlice[ConnectionPacketPreprocessEventHandler]
|
||||
messageExecBeforeEventHandlers *listings2.PrioritySlice[MessageExecBeforeEventHandler]
|
||||
messageReadyEventHandlers *listings2.PrioritySlice[MessageReadyEventHandler]
|
||||
deadlockDetectEventHandlers *listings2.PrioritySlice[OnDeadlockDetectEventHandler]
|
||||
|
||||
consoleCommandEventHandlers map[string]*slice.Priority[ConsoleCommandEventHandler]
|
||||
consoleCommandEventHandlers map[string]*listings2.PrioritySlice[ConsoleCommandEventHandler]
|
||||
consoleCommandEventHandlerInitOnce sync.Once
|
||||
}
|
||||
|
||||
// RegStopEvent 服务器停止时将立即执行被注册的事件处理函数
|
||||
func (slf *event) RegStopEvent(handler StopEventHandler, priority ...int) {
|
||||
slf.stopEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.stopEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -108,7 +109,7 @@ func (slf *event) RegConsoleCommandEvent(command string, handler ConsoleCommandE
|
|||
}
|
||||
|
||||
slf.consoleCommandEventHandlerInitOnce.Do(func() {
|
||||
slf.consoleCommandEventHandlers = map[string]*slice.Priority[ConsoleCommandEventHandler]{}
|
||||
slf.consoleCommandEventHandlers = map[string]*listings2.PrioritySlice[ConsoleCommandEventHandler]{}
|
||||
go func() {
|
||||
for {
|
||||
var input string
|
||||
|
@ -123,10 +124,10 @@ func (slf *event) RegConsoleCommandEvent(command string, handler ConsoleCommandE
|
|||
})
|
||||
list, exist := slf.consoleCommandEventHandlers[command]
|
||||
if !exist {
|
||||
list = slice.NewPriority[ConsoleCommandEventHandler]()
|
||||
list = listings2.NewPrioritySlice[ConsoleCommandEventHandler]()
|
||||
slf.consoleCommandEventHandlers[command] = list
|
||||
}
|
||||
list.Append(handler, slice.GetValue(priority, 0))
|
||||
list.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -161,7 +162,7 @@ func (slf *event) OnConsoleCommandEvent(command string, paramsStr string) {
|
|||
|
||||
// RegStartBeforeEvent 在服务器初始化完成启动前立刻执行被注册的事件处理函数
|
||||
func (slf *event) RegStartBeforeEvent(handler StartBeforeEventHandler, priority ...int) {
|
||||
slf.startBeforeEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.startBeforeEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -181,7 +182,7 @@ func (slf *event) OnStartBeforeEvent() {
|
|||
// RegStartFinishEvent 在服务器启动完成时将立刻执行被注册的事件处理函数
|
||||
// - 需要注意该时刻服务器已经启动完成,但是还有可能未开始处理消息,客户端有可能无法连接,如果需要在消息处理器准备就绪后执行,请使用 RegMessageReadyEvent 函数
|
||||
func (slf *event) RegStartFinishEvent(handler StartFinishEventHandler, priority ...int) {
|
||||
slf.startFinishEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.startFinishEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -205,7 +206,7 @@ func (slf *event) RegConnectionClosedEvent(handler ConnectionClosedEventHandler,
|
|||
if slf.network == NetworkHttp {
|
||||
panic(ErrNetworkIncompatibleHttp)
|
||||
}
|
||||
slf.connectionClosedEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.connectionClosedEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -226,7 +227,7 @@ func (slf *event) RegConnectionOpenedEvent(handler ConnectionOpenedEventHandler,
|
|||
if slf.network == NetworkHttp {
|
||||
panic(ErrNetworkIncompatibleHttp)
|
||||
}
|
||||
slf.connectionOpenedEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.connectionOpenedEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -246,7 +247,7 @@ func (slf *event) RegConnectionReceivePacketEvent(handler ConnectionReceivePacke
|
|||
if slf.network == NetworkHttp {
|
||||
panic(ErrNetworkIncompatibleHttp)
|
||||
}
|
||||
slf.connectionReceivePacketEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.connectionReceivePacketEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -262,7 +263,7 @@ func (slf *event) OnConnectionReceivePacketEvent(conn *Conn, packet []byte) {
|
|||
|
||||
// RegMessageErrorEvent 在处理消息发生错误时将立即执行被注册的事件处理函数
|
||||
func (slf *event) RegMessageErrorEvent(handler MessageErrorEventHandler, priority ...int) {
|
||||
slf.messageErrorEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.messageErrorEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -284,7 +285,7 @@ func (slf *event) OnMessageErrorEvent(message *Message, err error) {
|
|||
|
||||
// RegMessageLowExecEvent 在处理消息缓慢时将立即执行被注册的事件处理函数
|
||||
func (slf *event) RegMessageLowExecEvent(handler MessageLowExecEventHandler, priority ...int) {
|
||||
slf.messageLowExecEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.messageLowExecEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -305,7 +306,7 @@ func (slf *event) RegConnectionOpenedAfterEvent(handler ConnectionOpenedAfterEve
|
|||
if slf.network == NetworkHttp {
|
||||
panic(ErrNetworkIncompatibleHttp)
|
||||
}
|
||||
slf.connectionOpenedAfterEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.connectionOpenedAfterEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -323,7 +324,7 @@ func (slf *event) RegConnectionWritePacketBeforeEvent(handler ConnectionWritePac
|
|||
if slf.network == NetworkHttp {
|
||||
panic(ErrNetworkIncompatibleHttp)
|
||||
}
|
||||
slf.connectionWritePacketBeforeHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.connectionWritePacketBeforeHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -341,7 +342,7 @@ func (slf *event) OnConnectionWritePacketBeforeEvent(conn *Conn, packet []byte)
|
|||
|
||||
// RegShuntChannelCreatedEvent 在分流通道创建时将立刻执行被注册的事件处理函数
|
||||
func (slf *event) RegShuntChannelCreatedEvent(handler ShuntChannelCreatedEventHandler, priority ...int) {
|
||||
slf.shuntChannelCreatedEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.shuntChannelCreatedEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -356,7 +357,7 @@ func (slf *event) OnShuntChannelCreatedEvent(name string) {
|
|||
|
||||
// RegShuntChannelCloseEvent 在分流通道关闭时将立刻执行被注册的事件处理函数
|
||||
func (slf *event) RegShuntChannelCloseEvent(handler ShuntChannelClosedEventHandler, priority ...int) {
|
||||
slf.shuntChannelClosedEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.shuntChannelClosedEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -378,7 +379,7 @@ func (slf *event) OnShuntChannelClosedEvent(name string) {
|
|||
// - 数据包格式校验
|
||||
// - 数据包分包等情况处理
|
||||
func (slf *event) RegConnectionPacketPreprocessEvent(handler ConnectionPacketPreprocessEventHandler, priority ...int) {
|
||||
slf.connectionPacketPreprocessEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.connectionPacketPreprocessEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -402,7 +403,7 @@ func (slf *event) OnConnectionPacketPreprocessEvent(conn *Conn, packet []byte, u
|
|||
//
|
||||
// 适用于限流等场景
|
||||
func (slf *event) RegMessageExecBeforeEvent(handler MessageExecBeforeEventHandler, priority ...int) {
|
||||
slf.messageExecBeforeEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.messageExecBeforeEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||
}
|
||||
|
||||
|
@ -426,7 +427,7 @@ func (slf *event) OnMessageExecBeforeEvent(message *Message) bool {
|
|||
|
||||
// RegMessageReadyEvent 在服务器消息处理器准备就绪时立即执行被注册的事件处理函数
|
||||
func (slf *event) RegMessageReadyEvent(handler MessageReadyEventHandler, priority ...int) {
|
||||
slf.messageReadyEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.messageReadyEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
func (slf *event) OnMessageReadyEvent() {
|
||||
|
@ -447,7 +448,7 @@ func (slf *event) OnMessageReadyEvent() {
|
|||
|
||||
// RegDeadlockDetectEvent 在死锁检测触发时立即执行被注册的事件处理函数
|
||||
func (slf *event) RegDeadlockDetectEvent(handler OnDeadlockDetectEventHandler, priority ...int) {
|
||||
slf.deadlockDetectEventHandlers.Append(handler, slice.GetValue(priority, 0))
|
||||
slf.deadlockDetectEventHandlers.Append(handler, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
func (slf *event) OnDeadlockDetectEvent(message *Message) {
|
||||
|
|
|
@ -2,7 +2,8 @@ package gateway
|
|||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/server"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
listings2 "github.com/kercylan98/minotaur/utils/collection/listings"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -16,27 +17,27 @@ type (
|
|||
|
||||
func newEvents() *events {
|
||||
return &events{
|
||||
connectionOpenedEventHandles: slice.NewPriority[ConnectionOpenedEventHandle](),
|
||||
connectionClosedEventHandles: slice.NewPriority[ConnectionClosedEventHandle](),
|
||||
connectionReceivePacketEventHandles: slice.NewPriority[ConnectionReceivePacketEventHandle](),
|
||||
endpointConnectOpenedEventHandles: slice.NewPriority[EndpointConnectOpenedEventHandle](),
|
||||
endpointConnectClosedEventHandles: slice.NewPriority[EndpointConnectClosedEventHandle](),
|
||||
endpointConnectReceivePacketEventHandles: slice.NewPriority[EndpointConnectReceivePacketEventHandle](),
|
||||
connectionOpenedEventHandles: listings2.NewPrioritySlice[ConnectionOpenedEventHandle](),
|
||||
connectionClosedEventHandles: listings2.NewPrioritySlice[ConnectionClosedEventHandle](),
|
||||
connectionReceivePacketEventHandles: listings2.NewPrioritySlice[ConnectionReceivePacketEventHandle](),
|
||||
endpointConnectOpenedEventHandles: listings2.NewPrioritySlice[EndpointConnectOpenedEventHandle](),
|
||||
endpointConnectClosedEventHandles: listings2.NewPrioritySlice[EndpointConnectClosedEventHandle](),
|
||||
endpointConnectReceivePacketEventHandles: listings2.NewPrioritySlice[EndpointConnectReceivePacketEventHandle](),
|
||||
}
|
||||
}
|
||||
|
||||
type events struct {
|
||||
connectionOpenedEventHandles *slice.Priority[ConnectionOpenedEventHandle]
|
||||
connectionClosedEventHandles *slice.Priority[ConnectionClosedEventHandle]
|
||||
connectionReceivePacketEventHandles *slice.Priority[ConnectionReceivePacketEventHandle]
|
||||
endpointConnectOpenedEventHandles *slice.Priority[EndpointConnectOpenedEventHandle]
|
||||
endpointConnectClosedEventHandles *slice.Priority[EndpointConnectClosedEventHandle]
|
||||
endpointConnectReceivePacketEventHandles *slice.Priority[EndpointConnectReceivePacketEventHandle]
|
||||
connectionOpenedEventHandles *listings2.PrioritySlice[ConnectionOpenedEventHandle]
|
||||
connectionClosedEventHandles *listings2.PrioritySlice[ConnectionClosedEventHandle]
|
||||
connectionReceivePacketEventHandles *listings2.PrioritySlice[ConnectionReceivePacketEventHandle]
|
||||
endpointConnectOpenedEventHandles *listings2.PrioritySlice[EndpointConnectOpenedEventHandle]
|
||||
endpointConnectClosedEventHandles *listings2.PrioritySlice[EndpointConnectClosedEventHandle]
|
||||
endpointConnectReceivePacketEventHandles *listings2.PrioritySlice[EndpointConnectReceivePacketEventHandle]
|
||||
}
|
||||
|
||||
// RegConnectionOpenedEventHandle 注册客户端连接打开事件处理函数
|
||||
func (slf *events) RegConnectionOpenedEventHandle(handle ConnectionOpenedEventHandle, priority ...int) {
|
||||
slf.connectionOpenedEventHandles.Append(handle, slice.GetValue(priority, 0))
|
||||
slf.connectionOpenedEventHandles.Append(handle, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
func (slf *events) OnConnectionOpenedEvent(gateway *Gateway, conn *server.Conn) {
|
||||
|
@ -48,7 +49,7 @@ func (slf *events) OnConnectionOpenedEvent(gateway *Gateway, conn *server.Conn)
|
|||
|
||||
// RegConnectionClosedEventHandle 注册客户端连接关闭事件处理函数
|
||||
func (slf *events) RegConnectionClosedEventHandle(handle ConnectionClosedEventHandle, priority ...int) {
|
||||
slf.connectionClosedEventHandles.Append(handle, slice.GetValue(priority, 0))
|
||||
slf.connectionClosedEventHandles.Append(handle, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
func (slf *events) OnConnectionClosedEvent(gateway *Gateway, conn *server.Conn) {
|
||||
|
@ -60,7 +61,7 @@ func (slf *events) OnConnectionClosedEvent(gateway *Gateway, conn *server.Conn)
|
|||
|
||||
// RegConnectionReceivePacketEventHandle 注册客户端连接接收数据包事件处理函数
|
||||
func (slf *events) RegConnectionReceivePacketEventHandle(handle ConnectionReceivePacketEventHandle, priority ...int) {
|
||||
slf.connectionReceivePacketEventHandles.Append(handle, slice.GetValue(priority, 0))
|
||||
slf.connectionReceivePacketEventHandles.Append(handle, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
func (slf *events) OnConnectionReceivePacketEvent(gateway *Gateway, conn *server.Conn, packet []byte) {
|
||||
|
@ -72,7 +73,7 @@ func (slf *events) OnConnectionReceivePacketEvent(gateway *Gateway, conn *server
|
|||
|
||||
// RegEndpointConnectOpenedEventHandle 注册端点连接打开事件处理函数
|
||||
func (slf *events) RegEndpointConnectOpenedEventHandle(handle EndpointConnectOpenedEventHandle, priority ...int) {
|
||||
slf.endpointConnectOpenedEventHandles.Append(handle, slice.GetValue(priority, 0))
|
||||
slf.endpointConnectOpenedEventHandles.Append(handle, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
func (slf *events) OnEndpointConnectOpenedEvent(gateway *Gateway, endpoint *Endpoint) {
|
||||
|
@ -84,7 +85,7 @@ func (slf *events) OnEndpointConnectOpenedEvent(gateway *Gateway, endpoint *Endp
|
|||
|
||||
// RegEndpointConnectClosedEventHandle 注册端点连接关闭事件处理函数
|
||||
func (slf *events) RegEndpointConnectClosedEventHandle(handle EndpointConnectClosedEventHandle, priority ...int) {
|
||||
slf.endpointConnectClosedEventHandles.Append(handle, slice.GetValue(priority, 0))
|
||||
slf.endpointConnectClosedEventHandles.Append(handle, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
func (slf *events) OnEndpointConnectClosedEvent(gateway *Gateway, endpoint *Endpoint) {
|
||||
|
@ -96,7 +97,7 @@ func (slf *events) OnEndpointConnectClosedEvent(gateway *Gateway, endpoint *Endp
|
|||
|
||||
// RegEndpointConnectReceivePacketEventHandle 注册端点连接接收数据包事件处理函数
|
||||
func (slf *events) RegEndpointConnectReceivePacketEventHandle(handle EndpointConnectReceivePacketEventHandle, priority ...int) {
|
||||
slf.endpointConnectReceivePacketEventHandles.Append(handle, slice.GetValue(priority, 0))
|
||||
slf.endpointConnectReceivePacketEventHandles.Append(handle, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||
}
|
||||
|
||||
func (slf *events) OnEndpointConnectReceivePacketEvent(gateway *Gateway, endpoint *Endpoint, conn *server.Conn, packet []byte) {
|
||||
|
|
|
@ -2,7 +2,7 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -96,7 +96,7 @@ func (h *hub) IsOnline(id string) bool {
|
|||
// GetOnlineAll 获取所有在线连接
|
||||
func (h *hub) GetOnlineAll() map[string]*Conn {
|
||||
h.chanMutex.RLock()
|
||||
cop := hash.Copy(h.connections)
|
||||
cop := collection.CloneMap(h.connections)
|
||||
h.chanMutex.RUnlock()
|
||||
return cop
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ func (slf *Lockstep[ClientID, Command]) GetClientCount() int {
|
|||
func (slf *Lockstep[ClientID, Command]) DropCache(handler func(frame int64) bool) {
|
||||
slf.frameCacheLock.Lock()
|
||||
defer slf.frameCacheLock.Unlock()
|
||||
for frame, _ := range slf.frameCache {
|
||||
for frame := range slf.frameCache {
|
||||
if handler(frame) {
|
||||
delete(slf.frameCache, frame)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/super"
|
||||
)
|
||||
|
@ -70,7 +70,7 @@ type (
|
|||
|
||||
// HasMessageType 检查是否存在指定的消息类型
|
||||
func HasMessageType(mt MessageType) bool {
|
||||
return hash.Exist(messageNames, mt)
|
||||
return collection.FindInMapKey(messageNames, mt)
|
||||
}
|
||||
|
||||
// Message 服务器消息
|
||||
|
|
|
@ -4,9 +4,8 @@ import (
|
|||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/kercylan98/minotaur/server/internal/logger"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"github.com/kercylan98/minotaur/utils/super"
|
||||
"github.com/panjf2000/gnet"
|
||||
"github.com/xtaci/kcp-go/v5"
|
||||
|
@ -53,12 +52,12 @@ func init() {
|
|||
|
||||
// GetNetworks 获取所有支持的网络模式
|
||||
func GetNetworks() []Network {
|
||||
return slice.Copy(networks)
|
||||
return collection.CloneSlice(networks)
|
||||
}
|
||||
|
||||
// check 检查网络模式是否支持
|
||||
func (n Network) check() {
|
||||
if !hash.Exist(networkNameMap, string(n)) {
|
||||
if !collection.FindInMapKey(networkNameMap, string(n)) {
|
||||
panic(fmt.Errorf("unsupported network mode: %s", n))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/kercylan98/minotaur/server/internal/dispatcher"
|
||||
"github.com/kercylan98/minotaur/server/internal/logger"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
"github.com/kercylan98/minotaur/utils/network"
|
||||
"github.com/kercylan98/minotaur/utils/sher"
|
||||
"github.com/kercylan98/minotaur/utils/str"
|
||||
"github.com/kercylan98/minotaur/utils/super"
|
||||
"github.com/kercylan98/minotaur/utils/timer"
|
||||
|
@ -362,7 +362,7 @@ func (srv *Server) low(message *Message, present time.Time, expect time.Duration
|
|||
fields = append(fields, log.String("type", messageNames[message.t]), log.String("cost", cost.String()), log.String("message", message.String()))
|
||||
fields = append(fields, message.marks...)
|
||||
//fields = append(fields, log.Stack("stack"))
|
||||
log.Warn("ServerLowMessage", sher.ConvertSliceToAny(fields)...)
|
||||
log.Warn("ServerLowMessage", collection.ConvertSliceToAny(fields)...)
|
||||
srv.OnMessageLowExecEvent(message, cost)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package aoi
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/geometry"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
@ -54,7 +54,7 @@ func (slf *TwoDimensional[EID, PosType, E]) Refresh(entity E) {
|
|||
func (slf *TwoDimensional[EID, PosType, E]) GetFocus(id EID) map[EID]E {
|
||||
slf.rw.RLock()
|
||||
defer slf.rw.RUnlock()
|
||||
return hash.Copy(slf.focus[id])
|
||||
return collection.CloneMap(slf.focus[id])
|
||||
}
|
||||
|
||||
func (slf *TwoDimensional[EID, PosType, E]) SetSize(width, height int) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package arrangement
|
||||
|
||||
import "github.com/kercylan98/minotaur/utils/hash"
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
)
|
||||
|
||||
// Area 编排区域
|
||||
type Area[ID comparable, AreaInfo any] struct {
|
||||
|
@ -43,7 +45,7 @@ func (slf *Area[ID, AreaInfo]) IsAllow(item Item[ID]) (constraintErr error, conf
|
|||
|
||||
// IsConflict 检测一个成员是否会造成冲突
|
||||
func (slf *Area[ID, AreaInfo]) IsConflict(item Item[ID]) bool {
|
||||
if hash.Exist(slf.items, item.GetID()) {
|
||||
if collection.FindInMapKey(slf.items, item.GetID()) {
|
||||
return false
|
||||
}
|
||||
for _, conflict := range slf.conflicts {
|
||||
|
@ -56,7 +58,7 @@ func (slf *Area[ID, AreaInfo]) IsConflict(item Item[ID]) bool {
|
|||
|
||||
// GetConflictItems 获取与一个成员产生冲突的所有其他成员
|
||||
func (slf *Area[ID, AreaInfo]) GetConflictItems(item Item[ID]) map[ID]Item[ID] {
|
||||
if hash.Exist(slf.items, item.GetID()) {
|
||||
if collection.FindInMapKey(slf.items, item.GetID()) {
|
||||
return nil
|
||||
}
|
||||
var conflictItems map[ID]Item[ID]
|
||||
|
@ -79,7 +81,7 @@ func (slf *Area[ID, AreaInfo]) GetScore(extra ...Item[ID]) float64 {
|
|||
if slf.evaluate == nil {
|
||||
return 0
|
||||
}
|
||||
var items = hash.Copy(slf.items)
|
||||
var items = collection.CloneMap(slf.items)
|
||||
for _, item := range extra {
|
||||
items[item.GetID()] = item
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package arrangement
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"sort"
|
||||
)
|
||||
|
||||
|
@ -59,8 +59,8 @@ func (slf *Arrangement[ID, AreaInfo]) Arrange() (areas []*Area[ID, AreaInfo], no
|
|||
return slf.areas, slf.items
|
||||
}
|
||||
|
||||
var items = hash.Copy(slf.items)
|
||||
var fixed = hash.Copy(slf.fixed)
|
||||
var items = collection.CloneMap(slf.items)
|
||||
var fixed = collection.CloneMap(slf.fixed)
|
||||
|
||||
// 将固定编排的成员添加到对应的编排区域中,当成员无法添加到对应的编排区域中时,将会被转移至未编排区域
|
||||
for id, isFixed := range fixed {
|
||||
|
@ -94,7 +94,7 @@ func (slf *Arrangement[ID, AreaInfo]) Arrange() (areas []*Area[ID, AreaInfo], no
|
|||
}
|
||||
var editor = &Editor[ID, AreaInfo]{
|
||||
a: slf,
|
||||
pending: hash.ToSlice(items),
|
||||
pending: collection.ConvertMapValuesToSlice(items),
|
||||
falls: map[ID]struct{}{},
|
||||
}
|
||||
sort.Slice(editor.pending, func(i, j int) bool {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package arrangement
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"sort"
|
||||
)
|
||||
|
||||
|
@ -33,7 +32,7 @@ func (slf *Editor[ID, AreaInfo]) RemoveAreaItem(area *Area[ID, AreaInfo], item I
|
|||
|
||||
// AddAreaItem 将一个成员添加到编排区域中,如果该成员已经存在于编排区域中,则不进行任何操作
|
||||
func (slf *Editor[ID, AreaInfo]) AddAreaItem(area *Area[ID, AreaInfo], item Item[ID]) {
|
||||
if hash.Exist(slf.falls, item.GetID()) {
|
||||
if collection.FindInMapKey(slf.falls, item.GetID()) {
|
||||
return
|
||||
}
|
||||
area.items[item.GetID()] = item
|
||||
|
@ -42,12 +41,12 @@ func (slf *Editor[ID, AreaInfo]) AddAreaItem(area *Area[ID, AreaInfo], item Item
|
|||
|
||||
// GetAreas 获取所有的编排区域
|
||||
func (slf *Editor[ID, AreaInfo]) GetAreas() []*Area[ID, AreaInfo] {
|
||||
return slice.Copy(slf.a.areas)
|
||||
return collection.CloneSlice(slf.a.areas)
|
||||
}
|
||||
|
||||
// GetAreasWithScoreAsc 获取所有的编排区域,并按照分数升序排序
|
||||
func (slf *Editor[ID, AreaInfo]) GetAreasWithScoreAsc(extra ...Item[ID]) []*Area[ID, AreaInfo] {
|
||||
areas := slice.Copy(slf.a.areas)
|
||||
areas := collection.CloneSlice(slf.a.areas)
|
||||
sort.Slice(areas, func(i, j int) bool {
|
||||
return areas[i].GetScore(extra...) < areas[j].GetScore(extra...)
|
||||
})
|
||||
|
@ -56,7 +55,7 @@ func (slf *Editor[ID, AreaInfo]) GetAreasWithScoreAsc(extra ...Item[ID]) []*Area
|
|||
|
||||
// GetAreasWithScoreDesc 获取所有的编排区域,并按照分数降序排序
|
||||
func (slf *Editor[ID, AreaInfo]) GetAreasWithScoreDesc(extra ...Item[ID]) []*Area[ID, AreaInfo] {
|
||||
areas := slice.Copy(slf.a.areas)
|
||||
areas := collection.CloneSlice(slf.a.areas)
|
||||
sort.Slice(areas, func(i, j int) bool {
|
||||
return areas[i].GetScore(extra...) > areas[j].GetScore(extra...)
|
||||
})
|
||||
|
|
|
@ -1,22 +1,9 @@
|
|||
package sher
|
||||
package collection
|
||||
|
||||
import (
|
||||
"slices"
|
||||
)
|
||||
|
||||
// CloneMatrix 克隆二维数组
|
||||
func CloneMatrix[S ~[][]V, V any](slice S) S {
|
||||
if slice == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result = make(S, len(slice))
|
||||
for i := 0; i < len(slice); i++ {
|
||||
result[i] = CloneSlice(slice[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CloneSlice 克隆切片,该函数是 slices.Clone 的快捷方式
|
||||
func CloneSlice[S ~[]V, V any](slice S) S {
|
||||
return slices.Clone(slice)
|
||||
|
@ -35,11 +22,14 @@ func CloneMap[M ~map[K]V, K comparable, V any](m M) M {
|
|||
return result
|
||||
}
|
||||
|
||||
// CloneSliceN 克隆切片为 n 个切片进行返回
|
||||
// CloneSliceN 克隆 slice 为 n 个切片进行返回
|
||||
func CloneSliceN[S ~[]V, V any](slice S, n int) []S {
|
||||
if slice == nil {
|
||||
return nil
|
||||
}
|
||||
if n <= 0 {
|
||||
return []S{}
|
||||
}
|
||||
|
||||
var result = make([]S, n)
|
||||
for i := 0; i < n; i++ {
|
||||
|
@ -54,6 +44,10 @@ func CloneMapN[M ~map[K]V, K comparable, V any](m M, n int) []M {
|
|||
return nil
|
||||
}
|
||||
|
||||
if n <= 0 {
|
||||
return []M{}
|
||||
}
|
||||
|
||||
var result = make([]M, n)
|
||||
for i := 0; i < n; i++ {
|
||||
result[i] = CloneMap(m)
|
|
@ -0,0 +1,56 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
)
|
||||
|
||||
func ExampleCloneSlice() {
|
||||
var slice = []int{1, 2, 3}
|
||||
var result = collection.CloneSlice(slice)
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
}
|
||||
|
||||
func ExampleCloneMap() {
|
||||
var m = map[int]int{1: 1, 2: 2, 3: 3}
|
||||
var result = collection.CloneMap(m)
|
||||
fmt.Println(len(result))
|
||||
// Output:
|
||||
// 3
|
||||
}
|
||||
|
||||
func ExampleCloneSliceN() {
|
||||
var slice = []int{1, 2, 3}
|
||||
var result = collection.CloneSliceN(slice, 2)
|
||||
fmt.Println(len(result))
|
||||
// Output:
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleCloneMapN() {
|
||||
var m = map[int]int{1: 1, 2: 2, 3: 3}
|
||||
var result = collection.CloneMapN(m, 2)
|
||||
fmt.Println(len(result))
|
||||
// Output:
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleCloneSlices() {
|
||||
var slice1 = []int{1, 2, 3}
|
||||
var slice2 = []int{1, 2, 3}
|
||||
var result = collection.CloneSlices(slice1, slice2)
|
||||
fmt.Println(len(result))
|
||||
// Output:
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleCloneMaps() {
|
||||
var m1 = map[int]int{1: 1, 2: 2, 3: 3}
|
||||
var m2 = map[int]int{1: 1, 2: 2, 3: 3}
|
||||
var result = collection.CloneMaps(m1, m2)
|
||||
fmt.Println(len(result))
|
||||
// Output:
|
||||
// 2
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCloneSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected []int
|
||||
}{
|
||||
{"TestCloneSlice_NonEmptySlice", []int{1, 2, 3}, []int{1, 2, 3}},
|
||||
{"TestCloneSlice_EmptySlice", []int{}, []int{}},
|
||||
{"TestCloneSlice_NilSlice", nil, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.CloneSlice(c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
for i := 0; i < len(actual); i++ {
|
||||
if actual[i] != c.expected[i] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of input is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
expected map[int]int
|
||||
}{
|
||||
{"TestCloneMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{1: 1, 2: 2, 3: 3}},
|
||||
{"TestCloneMap_EmptyMap", map[int]int{}, map[int]int{}},
|
||||
{"TestCloneMap_NilMap", nil, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.CloneMap(c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of map is not equal")
|
||||
}
|
||||
for k, v := range actual {
|
||||
if v != c.expected[k] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of map is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneSliceN(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
inputN int
|
||||
expected [][]int
|
||||
}{
|
||||
{"TestCloneSliceN_NonEmptySlice", []int{1, 2, 3}, 2, [][]int{{1, 2, 3}, {1, 2, 3}}},
|
||||
{"TestCloneSliceN_EmptySlice", []int{}, 2, [][]int{{}, {}}},
|
||||
{"TestCloneSliceN_NilSlice", nil, 2, nil},
|
||||
{"TestCloneSliceN_NonEmptySlice_ZeroN", []int{1, 2, 3}, 0, [][]int{}},
|
||||
{"TestCloneSliceN_EmptySlice_ZeroN", []int{}, 0, [][]int{}},
|
||||
{"TestCloneSliceN_NilSlice_ZeroN", nil, 0, nil},
|
||||
{"TestCloneSliceN_NonEmptySlice_NegativeN", []int{1, 2, 3}, -1, [][]int{}},
|
||||
{"TestCloneSliceN_EmptySlice_NegativeN", []int{}, -1, [][]int{}},
|
||||
{"TestCloneSliceN_NilSlice_NegativeN", nil, -1, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.CloneSliceN(c.input, c.inputN)
|
||||
if actual == nil {
|
||||
if c.expected != nil {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, c.inputN, actual, "after clone, the expected is nil")
|
||||
}
|
||||
return
|
||||
}
|
||||
for a, i := range actual {
|
||||
for b, v := range i {
|
||||
if v != c.expected[a][b] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, c.inputN, actual, "after clone, the inputV of input is not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneMapN(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
inputN int
|
||||
expected []map[int]int
|
||||
}{
|
||||
{"TestCloneMapN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 2, []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 3: 3}}},
|
||||
{"TestCloneMapN_EmptyMap", map[int]int{}, 2, []map[int]int{{}, {}}},
|
||||
{"TestCloneMapN_NilMap", nil, 2, nil},
|
||||
{"TestCloneMapN_NonEmptyMap_ZeroN", map[int]int{1: 1, 2: 2, 3: 3}, 0, []map[int]int{}},
|
||||
{"TestCloneMapN_EmptyMap_ZeroN", map[int]int{}, 0, []map[int]int{}},
|
||||
{"TestCloneMapN_NilMap_ZeroN", nil, 0, nil},
|
||||
{"TestCloneMapN_NonEmptyMap_NegativeN", map[int]int{1: 1, 2: 2, 3: 3}, -1, []map[int]int{}},
|
||||
{"TestCloneMapN_EmptyMap_NegativeN", map[int]int{}, -1, []map[int]int{}},
|
||||
{"TestCloneMapN_NilMap_NegativeN", nil, -1, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.CloneMapN(c.input, c.inputN)
|
||||
if actual == nil {
|
||||
if c.expected != nil {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, actual, c.inputN, "after clone, the expected is nil")
|
||||
}
|
||||
return
|
||||
}
|
||||
for a, i := range actual {
|
||||
for b, v := range i {
|
||||
if v != c.expected[a][b] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, actual, c.inputN, "after clone, the inputV of map is not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input [][]int
|
||||
expected [][]int
|
||||
}{
|
||||
{"TestCloneSlices_NonEmptySlices", [][]int{{1, 2, 3}, {1, 2, 3}}, [][]int{{1, 2, 3}, {1, 2, 3}}},
|
||||
{"TestCloneSlices_EmptySlices", [][]int{{}, {}}, [][]int{{}, {}}},
|
||||
{"TestCloneSlices_NilSlices", nil, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.CloneSlices(c.input...)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
for a, i := range actual {
|
||||
for b, v := range i {
|
||||
if v != c.expected[a][b] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of input is not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneMaps(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []map[int]int
|
||||
expected []map[int]int
|
||||
}{
|
||||
{"TestCloneMaps_NonEmptyMaps", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 3: 3}}, []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 3: 3}}},
|
||||
{"TestCloneMaps_EmptyMaps", []map[int]int{{}, {}}, []map[int]int{{}, {}}},
|
||||
{"TestCloneMaps_NilMaps", nil, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.CloneMaps(c.input...)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of maps is not equal")
|
||||
}
|
||||
for a, i := range actual {
|
||||
for b, v := range i {
|
||||
if v != c.expected[a][b] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of maps is not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
package collection
|
||||
|
||||
type ComparisonHandler[V any] func(source, target V) bool
|
||||
|
||||
// InSlice 检查 v 是否被包含在 slice 中,当 handler 返回 true 时,表示 v 和 slice 中的某个元素相匹配
|
||||
func InSlice[S ~[]V, V any](slice S, v V, handler ComparisonHandler[V]) bool {
|
||||
if len(slice) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, value := range slice {
|
||||
if handler(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InComparableSlice 检查 v 是否被包含在 slice 中
|
||||
func InComparableSlice[S ~[]V, V comparable](slice S, v V) bool {
|
||||
if slice == nil {
|
||||
return false
|
||||
}
|
||||
for _, value := range slice {
|
||||
if value == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AllInSlice 检查 values 中的所有元素是否均被包含在 slice 中,当 handler 返回 true 时,表示 values 中的某个元素和 slice 中的某个元素相匹配
|
||||
// - 在所有 values 中的元素都被包含在 slice 中时,返回 true
|
||||
// - 当 values 长度为 0 或为 nil 时,将返回 true
|
||||
func AllInSlice[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
||||
if len(slice) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if !InSlice(slice, value, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AllInComparableSlice 检查 values 中的所有元素是否均被包含在 slice 中
|
||||
// - 在所有 values 中的元素都被包含在 slice 中时,返回 true
|
||||
// - 当 values 长度为 0 或为 nil 时,将返回 true
|
||||
func AllInComparableSlice[S ~[]V, V comparable](slice S, values []V) bool {
|
||||
if len(slice) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if !InComparableSlice(slice, value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyInSlice 检查 values 中的任意一个元素是否被包含在 slice 中,当 handler 返回 true 时,表示 value 中的某个元素和 slice 中的某个元素相匹配
|
||||
// - 当 values 中的任意一个元素被包含在 slice 中时,返回 true
|
||||
func AnyInSlice[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
||||
if len(slice) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if InSlice(slice, value, handler) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyInComparableSlice 检查 values 中的任意一个元素是否被包含在 slice 中
|
||||
// - 当 values 中的任意一个元素被包含在 slice 中时,返回 true
|
||||
func AnyInComparableSlice[S ~[]V, V comparable](slice S, values []V) bool {
|
||||
if len(slice) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if InComparableSlice(slice, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InSlices 通过将多个切片合并后检查 v 是否被包含在 slices 中,当 handler 返回 true 时,表示 v 和 slices 中的某个元素相匹配
|
||||
// - 当传入的 v 被包含在 slices 的任一成员中时,返回 true
|
||||
func InSlices[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
||||
return InSlice(MergeSlices(slices...), v, handler)
|
||||
}
|
||||
|
||||
// InComparableSlices 通过将多个切片合并后检查 v 是否被包含在 slices 中
|
||||
// - 当传入的 v 被包含在 slices 的任一成员中时,返回 true
|
||||
func InComparableSlices[S ~[]V, V comparable](slices []S, v V) bool {
|
||||
return InComparableSlice(MergeSlices(slices...), v)
|
||||
}
|
||||
|
||||
// AllInSlices 通过将多个切片合并后检查 values 中的所有元素是否被包含在 slices 中,当 handler 返回 true 时,表示 values 中的某个元素和 slices 中的某个元素相匹配
|
||||
// - 当 values 中的所有元素都被包含在 slices 的任一成员中时,返回 true
|
||||
func AllInSlices[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
||||
return AllInSlice(MergeSlices(slices...), values, handler)
|
||||
}
|
||||
|
||||
// AllInComparableSlices 通过将多个切片合并后检查 values 中的所有元素是否被包含在 slices 中
|
||||
// - 当 values 中的所有元素都被包含在 slices 的任一成员中时,返回 true
|
||||
func AllInComparableSlices[S ~[]V, V comparable](slices []S, values []V) bool {
|
||||
return AllInComparableSlice(MergeSlices(slices...), values)
|
||||
}
|
||||
|
||||
// AnyInSlices 通过将多个切片合并后检查 values 中的任意一个元素是否被包含在 slices 中,当 handler 返回 true 时,表示 values 中的某个元素和 slices 中的某个元素相匹配
|
||||
// - 当 values 中的任意一个元素被包含在 slices 的任一成员中时,返回 true
|
||||
func AnyInSlices[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
||||
return AnyInSlice(MergeSlices(slices...), values, handler)
|
||||
}
|
||||
|
||||
// AnyInComparableSlices 通过将多个切片合并后检查 values 中的任意一个元素是否被包含在 slices 中
|
||||
// - 当 values 中的任意一个元素被包含在 slices 的任一成员中时,返回 true
|
||||
func AnyInComparableSlices[S ~[]V, V comparable](slices []S, values []V) bool {
|
||||
return AnyInComparableSlice(MergeSlices(slices...), values)
|
||||
}
|
||||
|
||||
// InAllSlices 检查 v 是否被包含在 slices 的每一项元素中,当 handler 返回 true 时,表示 v 和 slices 中的某个元素相匹配
|
||||
// - 当 v 被包含在 slices 的每一项元素中时,返回 true
|
||||
func InAllSlices[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
||||
if len(slices) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, slice := range slices {
|
||||
if !InSlice(slice, v, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// InAllComparableSlices 检查 v 是否被包含在 slices 的每一项元素中
|
||||
// - 当 v 被包含在 slices 的每一项元素中时,返回 true
|
||||
func InAllComparableSlices[S ~[]V, V comparable](slices []S, v V) bool {
|
||||
if len(slices) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, slice := range slices {
|
||||
if !InComparableSlice(slice, v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyInAllSlices 检查 slices 中的每一个元素是否均包含至少任意一个 values 中的元素,当 handler 返回 true 时,表示 value 中的某个元素和 slices 中的某个元素相匹配
|
||||
// - 当 slices 中的每一个元素均包含至少任意一个 values 中的元素时,返回 true
|
||||
func AnyInAllSlices[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
||||
if len(slices) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, slice := range slices {
|
||||
if !AnyInSlice(slice, values, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyInAllComparableSlices 检查 slices 中的每一个元素是否均包含至少任意一个 values 中的元素
|
||||
// - 当 slices 中的每一个元素均包含至少任意一个 values 中的元素时,返回 true
|
||||
func AnyInAllComparableSlices[S ~[]V, V comparable](slices []S, values []V) bool {
|
||||
if len(slices) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, slice := range slices {
|
||||
if !AnyInComparableSlice(slice, values) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// KeyInMap 检查 m 中是否包含特定 key
|
||||
func KeyInMap[M ~map[K]V, K comparable, V any](m M, key K) bool {
|
||||
_, ok := m[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ValueInMap 检查 m 中是否包含特定 value,当 handler 返回 true 时,表示 value 和 m 中的某个元素相匹配
|
||||
func ValueInMap[M ~map[K]V, K comparable, V any](m M, value V, handler ComparisonHandler[V]) bool {
|
||||
if len(m) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, v := range m {
|
||||
if handler(value, v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AllKeyInMap 检查 m 中是否包含 keys 中所有的元素
|
||||
func AllKeyInMap[M ~map[K]V, K comparable, V any](m M, keys ...K) bool {
|
||||
if len(m) < len(keys) {
|
||||
return false
|
||||
}
|
||||
for _, key := range keys {
|
||||
if !KeyInMap(m, key) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AllValueInMap 检查 m 中是否包含 values 中所有的元素,当 handler 返回 true 时,表示 values 中的某个元素和 m 中的某个元素相匹配
|
||||
func AllValueInMap[M ~map[K]V, K comparable, V any](m M, values []V, handler ComparisonHandler[V]) bool {
|
||||
if len(m) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if !ValueInMap(m, value, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyKeyInMap 检查 m 中是否包含 keys 中任意一个元素
|
||||
func AnyKeyInMap[M ~map[K]V, K comparable, V any](m M, keys ...K) bool {
|
||||
if len(m) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, key := range keys {
|
||||
if KeyInMap(m, key) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyValueInMap 检查 m 中是否包含 values 中任意一个元素,当 handler 返回 true 时,表示 values 中的某个元素和 m 中的某个元素相匹配
|
||||
func AnyValueInMap[M ~map[K]V, K comparable, V any](m M, values []V, handler ComparisonHandler[V]) bool {
|
||||
if len(m) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if ValueInMap(m, value, handler) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AllKeyInMaps 检查 maps 中的每一个元素是否均包含 keys 中所有的元素
|
||||
func AllKeyInMaps[M ~map[K]V, K comparable, V any](maps []M, keys ...K) bool {
|
||||
if len(maps) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if !AllKeyInMap(m, keys...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AllValueInMaps 检查 maps 中的每一个元素是否均包含 value 中所有的元素,当 handler 返回 true 时,表示 value 中的某个元素和 maps 中的某个元素相匹配
|
||||
func AllValueInMaps[M ~map[K]V, K comparable, V any](maps []M, values []V, handler ComparisonHandler[V]) bool {
|
||||
if len(maps) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if !AllValueInMap(m, values, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyKeyInMaps 检查 keys 中的任意一个元素是否被包含在 maps 中的任意一个元素中
|
||||
// - 当 keys 中的任意一个元素被包含在 maps 中的任意一个元素中时,返回 true
|
||||
func AnyKeyInMaps[M ~map[K]V, K comparable, V any](maps []M, keys ...K) bool {
|
||||
if len(maps) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if AnyKeyInMap(m, keys...) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyValueInMaps 检查 maps 中的任意一个元素是否包含 value 中的任意一个元素,当 handler 返回 true 时,表示 value 中的某个元素和 maps 中的某个元素相匹配
|
||||
// - 当 maps 中的任意一个元素包含 value 中的任意一个元素时,返回 true
|
||||
func AnyValueInMaps[M ~map[K]V, K comparable, V any](maps []M, values []V, handler ComparisonHandler[V]) bool {
|
||||
if len(maps) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if !AnyValueInMap(m, values, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// KeyInAllMaps 检查 key 是否被包含在 maps 的每一个元素中
|
||||
func KeyInAllMaps[M ~map[K]V, K comparable, V any](maps []M, key K) bool {
|
||||
if len(maps) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if !KeyInMap(m, key) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyKeyInAllMaps 检查 maps 中的每一个元素是否均包含 keys 中任意一个元素
|
||||
// - 当 maps 中的每一个元素均包含 keys 中任意一个元素时,返回 true
|
||||
func AnyKeyInAllMaps[M ~map[K]V, K comparable, V any](maps []M, keys []K) bool {
|
||||
if len(maps) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if !AnyKeyInMap(m, keys...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
)
|
||||
|
||||
func ExampleInSlice() {
|
||||
result := collection.InSlice([]int{1, 2, 3}, 2, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleInComparableSlice() {
|
||||
result := collection.InComparableSlice([]int{1, 2, 3}, 2)
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAllInSlice() {
|
||||
result := collection.AllInSlice([]int{1, 2, 3}, []int{1, 2}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAllInComparableSlice() {
|
||||
result := collection.AllInComparableSlice([]int{1, 2, 3}, []int{1, 2})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnyInSlice() {
|
||||
result := collection.AnyInSlice([]int{1, 2, 3}, []int{1, 2}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnyInComparableSlice() {
|
||||
result := collection.AnyInComparableSlice([]int{1, 2, 3}, []int{1, 2})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleInSlices() {
|
||||
result := collection.InSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleInComparableSlices() {
|
||||
result := collection.InComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2)
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAllInSlices() {
|
||||
result := collection.AllInSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAllInComparableSlices() {
|
||||
result := collection.AllInComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnyInSlices() {
|
||||
result := collection.AnyInSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnyInComparableSlices() {
|
||||
result := collection.AnyInComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleInAllSlices() {
|
||||
result := collection.InAllSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleInAllComparableSlices() {
|
||||
result := collection.InAllComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2)
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleAnyInAllSlices() {
|
||||
result := collection.AnyInAllSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleAnyInAllComparableSlices() {
|
||||
result := collection.AnyInAllComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleKeyInMap() {
|
||||
result := collection.KeyInMap(map[string]int{"a": 1, "b": 2}, "a")
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleValueInMap() {
|
||||
result := collection.ValueInMap(map[string]int{"a": 1, "b": 2}, 2, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAllKeyInMap() {
|
||||
result := collection.AllKeyInMap(map[string]int{"a": 1, "b": 2}, "a", "b")
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAllValueInMap() {
|
||||
result := collection.AllValueInMap(map[string]int{"a": 1, "b": 2}, []int{1}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnyKeyInMap() {
|
||||
result := collection.AnyKeyInMap(map[string]int{"a": 1, "b": 2}, "a", "b")
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnyValueInMap() {
|
||||
result := collection.AnyValueInMap(map[string]int{"a": 1, "b": 2}, []int{1}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAllKeyInMaps() {
|
||||
result := collection.AllKeyInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, "a", "b")
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAllValueInMaps() {
|
||||
result := collection.AllValueInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, []int{1}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnyKeyInMaps() {
|
||||
result := collection.AnyKeyInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, "a", "b")
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnyValueInMaps() {
|
||||
result := collection.AnyValueInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, []int{1}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleKeyInAllMaps() {
|
||||
result := collection.KeyInAllMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, "a")
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleAnyKeyInAllMaps() {
|
||||
result := collection.AnyKeyInAllMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, []string{"a"})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// true
|
||||
}
|
|
@ -0,0 +1,709 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var intComparisonHandler = func(source, target int) bool {
|
||||
return source == target
|
||||
}
|
||||
|
||||
func TestInSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
inputV int
|
||||
expected bool
|
||||
}{
|
||||
{"TestInSlice_NonEmptySliceIn", []int{1, 2, 3}, 1, true},
|
||||
{"TestInSlice_NonEmptySliceNotIn", []int{1, 2, 3}, 4, false},
|
||||
{"TestInSlice_EmptySlice", []int{}, 1, false},
|
||||
{"TestInSlice_NilSlice", nil, 1, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.InSlice(c.input, c.inputV, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInComparableSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
inputV int
|
||||
expected bool
|
||||
}{
|
||||
{"TestInComparableSlice_NonEmptySliceIn", []int{1, 2, 3}, 1, true},
|
||||
{"TestInComparableSlice_NonEmptySliceNotIn", []int{1, 2, 3}, 4, false},
|
||||
{"TestInComparableSlice_EmptySlice", []int{}, 1, false},
|
||||
{"TestInComparableSlice_NilSlice", nil, 1, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.InComparableSlice(c.input, c.inputV)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllInSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
inputV []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAllInSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true},
|
||||
{"TestAllInSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, false},
|
||||
{"TestAllInSlice_EmptySlice", []int{}, []int{1, 2}, false},
|
||||
{"TestAllInSlice_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAllInSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, true},
|
||||
{"TestAllInSlice_NilValueSlice", []int{1, 2, 3}, nil, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AllInSlice(c.input, c.inputV, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllInComparableSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
inputV []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAllInComparableSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true},
|
||||
{"TestAllInComparableSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, false},
|
||||
{"TestAllInComparableSlice_EmptySlice", []int{}, []int{1, 2}, false},
|
||||
{"TestAllInComparableSlice_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAllInComparableSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, true},
|
||||
{"TestAllInComparableSlice_NilValueSlice", []int{1, 2, 3}, nil, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AllInComparableSlice(c.input, c.inputV)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyInSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
inputV []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyInSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true},
|
||||
{"TestAnyInSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, true},
|
||||
{"TestAnyInSlice_EmptySlice", []int{}, []int{1, 2}, false},
|
||||
{"TestAnyInSlice_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAnyInSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, false},
|
||||
{"TestAnyInSlice_NilValueSlice", []int{1, 2, 3}, nil, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyInSlice(c.input, c.inputV, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyInComparableSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
inputV []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyInComparableSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true},
|
||||
{"TestAnyInComparableSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, true},
|
||||
{"TestAnyInComparableSlice_EmptySlice", []int{}, []int{1, 2}, false},
|
||||
{"TestAnyInComparableSlice_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAnyInComparableSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, false},
|
||||
{"TestAnyInComparableSlice_NilValueSlice", []int{1, 2, 3}, nil, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyInComparableSlice(c.input, c.inputV)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input [][]int
|
||||
inputV int
|
||||
expected bool
|
||||
}{
|
||||
{"TestInSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, 1, true},
|
||||
{"TestInSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false},
|
||||
{"TestInSlices_EmptySlice", [][]int{{}, {}}, 1, false},
|
||||
{"TestInSlices_NilSlice", nil, 1, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.InSlices(c.input, c.inputV, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInComparableSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input [][]int
|
||||
inputV int
|
||||
expected bool
|
||||
}{
|
||||
{"TestInComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, 1, true},
|
||||
{"TestInComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false},
|
||||
{"TestInComparableSlices_EmptySlice", [][]int{{}, {}}, 1, false},
|
||||
{"TestInComparableSlices_NilSlice", nil, 1, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.InComparableSlices(c.input, c.inputV)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllInSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input [][]int
|
||||
inputValues []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAllInSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true},
|
||||
{"TestAllInSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false},
|
||||
{"TestAllInSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAllInSlices_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAllInSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, true},
|
||||
{"TestAllInSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AllInSlices(c.input, c.inputValues, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllInComparableSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input [][]int
|
||||
inputValues []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAllInComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true},
|
||||
{"TestAllInComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false},
|
||||
{"TestAllInComparableSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAllInComparableSlices_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAllInComparableSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, true},
|
||||
{"TestAllInComparableSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AllInComparableSlices(c.input, c.inputValues)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyInSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
slices [][]int
|
||||
values []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyInSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true},
|
||||
{"TestAnyInSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, true},
|
||||
{"TestAnyInSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAnyInSlices_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAnyInSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false},
|
||||
{"TestAnyInSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyInSlices(c.slices, c.values, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyInComparableSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
slices [][]int
|
||||
values []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyInComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true},
|
||||
{"TestAnyInComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, true},
|
||||
{"TestAnyInComparableSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAnyInComparableSlices_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAnyInComparableSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false},
|
||||
{"TestAnyInComparableSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyInComparableSlices(c.slices, c.values)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInAllSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
slices [][]int
|
||||
value int
|
||||
expected bool
|
||||
}{
|
||||
{"TestInAllSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, 1, true},
|
||||
{"TestInAllSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false},
|
||||
{"TestInAllSlices_EmptySlice", [][]int{{}, {}}, 1, false},
|
||||
{"TestInAllSlices_NilSlice", nil, 1, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.InAllSlices(c.slices, c.value, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInAllComparableSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
slices [][]int
|
||||
value int
|
||||
expected bool
|
||||
}{
|
||||
{"TestInAllComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, 1, true},
|
||||
{"TestInAllComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false},
|
||||
{"TestInAllComparableSlices_EmptySlice", [][]int{{}, {}}, 1, false},
|
||||
{"TestInAllComparableSlices_NilSlice", nil, 1, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.InAllComparableSlices(c.slices, c.value)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyInAllSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
slices [][]int
|
||||
values []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyInAllSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, []int{1, 2}, true},
|
||||
{"TestAnyInAllSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false},
|
||||
{"TestAnyInAllSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAnyInAllSlices_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAnyInAllSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false},
|
||||
{"TestAnyInAllSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyInAllSlices(c.slices, c.values, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyInAllComparableSlices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
slices [][]int
|
||||
values []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyInAllComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, []int{1, 2}, true},
|
||||
{"TestAnyInAllComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false},
|
||||
{"TestAnyInAllComparableSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAnyInAllComparableSlices_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAnyInAllComparableSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false},
|
||||
{"TestAnyInAllComparableSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyInAllComparableSlices(c.slices, c.values)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyInMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
m map[int]int
|
||||
key int
|
||||
expected bool
|
||||
}{
|
||||
{"TestKeyInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, 1, true},
|
||||
{"TestKeyInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, 3, false},
|
||||
{"TestKeyInMap_EmptySlice", map[int]int{}, 1, false},
|
||||
{"TestKeyInMap_NilSlice", nil, 1, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.KeyInMap(c.m, c.key)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueInMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
m map[int]int
|
||||
value int
|
||||
handler collection.ComparisonHandler[int]
|
||||
expected bool
|
||||
}{
|
||||
{"TestValueInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, 1, intComparisonHandler, true},
|
||||
{"TestValueInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, 3, intComparisonHandler, false},
|
||||
{"TestValueInMap_EmptySlice", map[int]int{}, 1, intComparisonHandler, false},
|
||||
{"TestValueInMap_NilSlice", nil, 1, intComparisonHandler, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.ValueInMap(c.m, c.value, c.handler)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllKeyInMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
m map[int]int
|
||||
keys []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAllKeyInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true},
|
||||
{"TestAllKeyInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, false},
|
||||
{"TestAllKeyInMap_EmptySlice", map[int]int{}, []int{1, 2}, false},
|
||||
{"TestAllKeyInMap_NilSlice", nil, []int{1, 2}, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AllKeyInMap(c.m, c.keys...)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllValueInMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
m map[int]int
|
||||
values []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAllValueInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true},
|
||||
{"TestAllValueInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, false},
|
||||
{"TestAllValueInMap_EmptySlice", map[int]int{}, []int{1, 2}, false},
|
||||
{"TestAllValueInMap_NilSlice", nil, []int{1, 2}, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AllValueInMap(c.m, c.values, intComparisonHandler)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyKeyInMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
m map[int]int
|
||||
keys []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyKeyInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true},
|
||||
{"TestAnyKeyInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, true},
|
||||
{"TestAnyKeyInMap_EmptySlice", map[int]int{}, []int{1, 2}, false},
|
||||
{"TestAnyKeyInMap_NilSlice", nil, []int{1, 2}, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyKeyInMap(c.m, c.keys...)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyValueInMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
m map[int]int
|
||||
values []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyValueInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true},
|
||||
{"TestAnyValueInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, true},
|
||||
{"TestAnyValueInMap_EmptySlice", map[int]int{}, []int{1, 2}, false},
|
||||
{"TestAnyValueInMap_NilSlice", nil, []int{1, 2}, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyValueInMap(c.m, c.values, intComparisonHandler)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllKeyInMaps(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
maps []map[int]int
|
||||
keys []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAllKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false},
|
||||
{"TestAllKeyInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, false},
|
||||
{"TestAllKeyInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false},
|
||||
{"TestAllKeyInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAllKeyInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false},
|
||||
{"TestAllKeyInMaps_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAllKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AllKeyInMaps(c.maps, c.keys...)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllValueInMaps(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
maps []map[int]int
|
||||
values []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAllValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false},
|
||||
{"TestAllValueInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, false},
|
||||
{"TestAllValueInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false},
|
||||
{"TestAllValueInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAllValueInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false},
|
||||
{"TestAllValueInMaps_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAllValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AllValueInMaps(c.maps, c.values, intComparisonHandler)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyKeyInMaps(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
maps []map[int]int
|
||||
keys []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, true},
|
||||
{"TestAnyKeyInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, true},
|
||||
{"TestAnyKeyInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, true},
|
||||
{"TestAnyKeyInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAnyKeyInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false},
|
||||
{"TestAnyKeyInMaps_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAnyKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyKeyInMaps(c.maps, c.keys...)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyValueInMaps(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
maps []map[int]int
|
||||
values []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false},
|
||||
{"TestAnyValueInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, true},
|
||||
{"TestAnyValueInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false},
|
||||
{"TestAnyValueInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAnyValueInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false},
|
||||
{"TestAnyValueInMaps_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAnyValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyValueInMaps(c.maps, c.values, intComparisonHandler)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyInAllMaps(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
maps []map[int]int
|
||||
key int
|
||||
expected bool
|
||||
}{
|
||||
{"TestKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, 1, false},
|
||||
{"TestKeyInAllMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, 3, false},
|
||||
{"TestKeyInAllMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, 1, false},
|
||||
{"TestKeyInAllMaps_NilSlice", []map[int]int{{}, {}}, 1, false},
|
||||
{"TestKeyInAllMaps_EmptySlice", []map[int]int{}, 1, false},
|
||||
{"TestKeyInAllMaps_NilSlice", nil, 1, false},
|
||||
{"TestKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, 1, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.KeyInAllMaps(c.maps, c.key)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyKeyInAllMaps(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
maps []map[int]int
|
||||
keys []int
|
||||
expected bool
|
||||
}{
|
||||
{"TestAnyKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false},
|
||||
{"TestAnyKeyInAllMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, true},
|
||||
{"TestAnyKeyInAllMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false},
|
||||
{"TestAnyKeyInAllMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false},
|
||||
{"TestAnyKeyInAllMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false},
|
||||
{"TestAnyKeyInAllMaps_NilSlice", nil, []int{1, 2}, false},
|
||||
{"TestAnyKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual = collection.AnyKeyInAllMaps(c.maps, c.keys)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
package sher
|
||||
package collection
|
||||
|
||||
// ConvertSliceToAny 将切片转换为任意类型的切片
|
||||
func ConvertSliceToAny[S ~[]V, V any](s S) []any {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
var r = make([]any, len(s))
|
||||
for i, v := range s {
|
||||
r[i] = v
|
||||
|
@ -11,6 +14,9 @@ func ConvertSliceToAny[S ~[]V, V any](s S) []any {
|
|||
|
||||
// ConvertSliceToIndexMap 将切片转换为索引为键的映射
|
||||
func ConvertSliceToIndexMap[S ~[]V, V any](s S) map[int]V {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
var r = make(map[int]V, len(s))
|
||||
for i, v := range s {
|
||||
r[i] = v
|
||||
|
@ -20,6 +26,9 @@ func ConvertSliceToIndexMap[S ~[]V, V any](s S) map[int]V {
|
|||
|
||||
// ConvertSliceToMap 将切片转换为值为键的映射
|
||||
func ConvertSliceToMap[S ~[]V, V comparable](s S) map[V]struct{} {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
var r = make(map[V]struct{}, len(s))
|
||||
for _, v := range s {
|
||||
r[v] = struct{}{}
|
||||
|
@ -29,6 +38,9 @@ func ConvertSliceToMap[S ~[]V, V comparable](s S) map[V]struct{} {
|
|||
|
||||
// ConvertSliceToBoolMap 将切片转换为值为键的映射
|
||||
func ConvertSliceToBoolMap[S ~[]V, V comparable](s S) map[V]bool {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
var r = make(map[V]bool, len(s))
|
||||
for _, v := range s {
|
||||
r[v] = true
|
||||
|
@ -38,6 +50,9 @@ func ConvertSliceToBoolMap[S ~[]V, V comparable](s S) map[V]bool {
|
|||
|
||||
// ConvertMapKeysToSlice 将映射的键转换为切片
|
||||
func ConvertMapKeysToSlice[M ~map[K]V, K comparable, V any](m M) []K {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
var r = make([]K, 0, len(m))
|
||||
for k := range m {
|
||||
r = append(r, k)
|
||||
|
@ -47,9 +62,48 @@ func ConvertMapKeysToSlice[M ~map[K]V, K comparable, V any](m M) []K {
|
|||
|
||||
// ConvertMapValuesToSlice 将映射的值转换为切片
|
||||
func ConvertMapValuesToSlice[M ~map[K]V, K comparable, V any](m M) []V {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
var r = make([]V, 0, len(m))
|
||||
for _, v := range m {
|
||||
r = append(r, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// InvertMap 将映射的键和值互换
|
||||
func InvertMap[M ~map[K]V, N ~map[V]K, K, V comparable](m M) N {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
var r = make(N, len(m))
|
||||
for k, v := range m {
|
||||
r[v] = k
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// ConvertMapValuesToBool 将映射的值转换为布尔值
|
||||
func ConvertMapValuesToBool[M ~map[K]V, N ~map[K]bool, K comparable, V any](m M) N {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
var r = make(N, len(m))
|
||||
for k := range m {
|
||||
r[k] = true
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// ReverseSlice 将切片反转
|
||||
func ReverseSlice[S ~[]V, V any](s *S) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var length = len(*s)
|
||||
for i := 0; i < length/2; i++ {
|
||||
(*s)[i], (*s)[length-i-1] = (*s)[length-i-1], (*s)[i]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package collection_test
|
|
@ -0,0 +1,264 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConvertSliceToAny(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected []interface{}
|
||||
}{
|
||||
{name: "TestConvertSliceToAny_NonEmpty", input: []int{1, 2, 3}, expected: []any{1, 2, 3}},
|
||||
{name: "TestConvertSliceToAny_Empty", input: []int{}, expected: []any{}},
|
||||
{name: "TestConvertSliceToAny_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.ConvertSliceToAny(c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
for i := 0; i < len(actual); i++ {
|
||||
av, ev := actual[i], c.expected[i]
|
||||
if reflect.TypeOf(av).Kind() != reflect.TypeOf(ev).Kind() {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
if av != ev {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertSliceToIndexMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected map[int]int
|
||||
}{
|
||||
{name: "TestConvertSliceToIndexMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]int{0: 1, 1: 2, 2: 3}},
|
||||
{name: "TestConvertSliceToIndexMap_Empty", input: []int{}, expected: map[int]int{}},
|
||||
{name: "TestConvertSliceToIndexMap_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.ConvertSliceToIndexMap(c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
for k, v := range actual {
|
||||
if c.expected[k] != v {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertSliceToMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected map[int]struct{}
|
||||
}{
|
||||
{name: "TestConvertSliceToMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]struct{}{1: {}, 2: {}, 3: {}}},
|
||||
{name: "TestConvertSliceToMap_Empty", input: []int{}, expected: map[int]struct{}{}},
|
||||
{name: "TestConvertSliceToMap_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.ConvertSliceToMap(c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
for k, v := range actual {
|
||||
if _, ok := c.expected[k]; !ok {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
if v != struct{}{} {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertSliceToBoolMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected map[int]bool
|
||||
}{
|
||||
{name: "TestConvertSliceToBoolMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]bool{1: true, 2: true, 3: true}},
|
||||
{name: "TestConvertSliceToBoolMap_Empty", input: []int{}, expected: map[int]bool{}},
|
||||
{name: "TestConvertSliceToBoolMap_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.ConvertSliceToBoolMap(c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
for k, v := range actual {
|
||||
if c.expected[k] != v {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertMapKeysToSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
expected []int
|
||||
}{
|
||||
{name: "TestConvertMapKeysToSlice_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, expected: []int{1, 2, 3}},
|
||||
{name: "TestConvertMapKeysToSlice_Empty", input: map[int]int{}, expected: []int{}},
|
||||
{name: "TestConvertMapKeysToSlice_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.ConvertMapKeysToSlice(c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
var matchCount = 0
|
||||
for _, av := range actual {
|
||||
for _, ev := range c.expected {
|
||||
if av == ev {
|
||||
matchCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
if matchCount != len(actual) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertMapValuesToSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
expected []int
|
||||
}{
|
||||
{name: "TestConvertMapValuesToSlice_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, expected: []int{1, 2, 3}},
|
||||
{name: "TestConvertMapValuesToSlice_Empty", input: map[int]int{}, expected: []int{}},
|
||||
{name: "TestConvertMapValuesToSlice_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.ConvertMapValuesToSlice(c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
var matchCount = 0
|
||||
for _, av := range actual {
|
||||
for _, ev := range c.expected {
|
||||
if av == ev {
|
||||
matchCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
if matchCount != len(actual) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvertMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]string
|
||||
expected map[string]int
|
||||
}{
|
||||
{name: "TestInvertMap_NonEmpty", input: map[int]string{1: "1", 2: "2", 3: "3"}, expected: map[string]int{"1": 1, "2": 2, "3": 3}},
|
||||
{name: "TestInvertMap_Empty", input: map[int]string{}, expected: map[string]int{}},
|
||||
{name: "TestInvertMap_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.InvertMap[map[int]string, map[string]int](c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
for k, v := range actual {
|
||||
if c.expected[k] != v {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertMapValuesToBool(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
expected map[int]bool
|
||||
}{
|
||||
{name: "TestConvertMapValuesToBool_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, expected: map[int]bool{1: true, 2: true, 3: true}},
|
||||
{name: "TestConvertMapValuesToBool_Empty", input: map[int]int{}, expected: map[int]bool{}},
|
||||
{name: "TestConvertMapValuesToBool_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.ConvertMapValuesToBool[map[int]int, map[int]bool](c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
for k, v := range actual {
|
||||
if c.expected[k] != v {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected []int
|
||||
}{
|
||||
{name: "TestReverseSlice_NonEmpty", input: []int{1, 2, 3}, expected: []int{3, 2, 1}},
|
||||
{name: "TestReverseSlice_Empty", input: []int{}, expected: []int{}},
|
||||
{name: "TestReverseSlice_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
collection.ReverseSlice(&c.input)
|
||||
if len(c.input) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||
}
|
||||
for i := 0; i < len(c.input); i++ {
|
||||
av, ev := c.input[i], c.expected[i]
|
||||
if reflect.TypeOf(av).Kind() != reflect.TypeOf(ev).Kind() {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||
}
|
||||
if av != ev {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
// Package collection 用于对 input 和 map 操作的工具函数
|
||||
package collection
|
|
@ -0,0 +1,82 @@
|
|||
package collection
|
||||
|
||||
// ClearSlice 清空切片
|
||||
func ClearSlice[S ~[]V, V any](slice *S) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
*slice = (*slice)[0:0]
|
||||
}
|
||||
|
||||
// ClearMap 清空 map
|
||||
func ClearMap[M ~map[K]V, K comparable, V any](m M) {
|
||||
for k := range m {
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
|
||||
// DropSliceByIndices 删除切片中特定索引的元素
|
||||
func DropSliceByIndices[S ~[]V, V any](slice *S, indices ...int) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
if len(indices) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
excludeMap := make(map[int]bool)
|
||||
for _, ex := range indices {
|
||||
excludeMap[ex] = true
|
||||
}
|
||||
|
||||
validElements := (*slice)[:0]
|
||||
for i, v := range *slice {
|
||||
if !excludeMap[i] {
|
||||
validElements = append(validElements, v)
|
||||
}
|
||||
}
|
||||
|
||||
*slice = validElements
|
||||
}
|
||||
|
||||
// DropSliceByCondition 删除切片中符合条件的元素
|
||||
// - condition 的返回值为 true 时,将会删除该元素
|
||||
func DropSliceByCondition[S ~[]V, V any](slice *S, condition func(v V) bool) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
if condition == nil {
|
||||
return
|
||||
}
|
||||
|
||||
validElements := (*slice)[:0]
|
||||
for _, v := range *slice {
|
||||
if !condition(v) {
|
||||
validElements = append(validElements, v)
|
||||
}
|
||||
}
|
||||
|
||||
*slice = validElements
|
||||
}
|
||||
|
||||
// DropSliceOverlappingElements 删除切片中与另一个切片重叠的元素
|
||||
func DropSliceOverlappingElements[S ~[]V, V any](slice *S, anotherSlice []V, comparisonHandler ComparisonHandler[V]) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
if anotherSlice == nil {
|
||||
return
|
||||
}
|
||||
if comparisonHandler == nil {
|
||||
return
|
||||
}
|
||||
|
||||
validElements := (*slice)[:0]
|
||||
for _, v := range *slice {
|
||||
if !InSlice(anotherSlice, v, comparisonHandler) {
|
||||
validElements = append(validElements, v)
|
||||
}
|
||||
}
|
||||
|
||||
*slice = validElements
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package collection
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleClearSlice() {
|
||||
slice := []int{1, 2, 3, 4, 5}
|
||||
ClearSlice(&slice)
|
||||
fmt.Println(slice)
|
||||
// Output:
|
||||
// []
|
||||
}
|
||||
|
||||
func ExampleClearMap() {
|
||||
m := map[int]int{1: 1, 2: 2, 3: 3}
|
||||
ClearMap(m)
|
||||
fmt.Println(m)
|
||||
// Output:
|
||||
// map[]
|
||||
}
|
||||
|
||||
func ExampleDropSliceByIndices() {
|
||||
slice := []int{1, 2, 3, 4, 5}
|
||||
DropSliceByIndices(&slice, 1, 3)
|
||||
fmt.Println(slice)
|
||||
// Output:
|
||||
// [1 3 5]
|
||||
}
|
||||
|
||||
func ExampleDropSliceByCondition() {
|
||||
slice := []int{1, 2, 3, 4, 5}
|
||||
DropSliceByCondition(&slice, func(v int) bool {
|
||||
return v%2 == 0
|
||||
})
|
||||
fmt.Println(slice)
|
||||
// Output:
|
||||
// [1 3 5]
|
||||
}
|
||||
|
||||
func ExampleDropSliceOverlappingElements() {
|
||||
slice := []int{1, 2, 3, 4, 5}
|
||||
DropSliceOverlappingElements(&slice, []int{1, 3, 5}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(slice)
|
||||
// Output:
|
||||
// [2 4]
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClearSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected []int
|
||||
}{
|
||||
{"TestClearSlice_NonEmptySlice", []int{1, 2, 3}, []int{}},
|
||||
{"TestClearSlice_EmptySlice", []int{}, []int{}},
|
||||
{"TestClearSlice_NilSlice", nil, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
collection.ClearSlice(&c.input)
|
||||
if len(c.input) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the length of input is not equal")
|
||||
}
|
||||
for i := 0; i < len(c.input); i++ {
|
||||
if c.input[i] != c.expected[i] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the inputV of input is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
expected map[int]int
|
||||
}{
|
||||
{"TestClearMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{}},
|
||||
{"TestClearMap_EmptyMap", map[int]int{}, map[int]int{}},
|
||||
{"TestClearMap_NilMap", nil, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
collection.ClearMap(c.input)
|
||||
if len(c.input) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the length of map is not equal")
|
||||
}
|
||||
for k, v := range c.input {
|
||||
if v != c.expected[k] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the inputV of map is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropSliceByIndices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
indices []int
|
||||
expected []int
|
||||
}{
|
||||
{"TestDropSliceByIndices_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{1, 3}, []int{1, 3, 5}},
|
||||
{"TestDropSliceByIndices_EmptySlice", []int{}, []int{1, 3}, []int{}},
|
||||
{"TestDropSliceByIndices_NilSlice", nil, []int{1, 3}, nil},
|
||||
{"TestDropSliceByIndices_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{}, []int{1, 2, 3, 4, 5}},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
collection.DropSliceByIndices(&c.input, c.indices...)
|
||||
if len(c.input) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the length of input is not equal")
|
||||
}
|
||||
for i := 0; i < len(c.input); i++ {
|
||||
if c.input[i] != c.expected[i] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the inputV of input is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropSliceByCondition(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
condition func(v int) bool
|
||||
expected []int
|
||||
}{
|
||||
{"TestDropSliceByCondition_NonEmptySlice", []int{1, 2, 3, 4, 5}, func(v int) bool { return v%2 == 0 }, []int{1, 3, 5}},
|
||||
{"TestDropSliceByCondition_EmptySlice", []int{}, func(v int) bool { return v%2 == 0 }, []int{}},
|
||||
{"TestDropSliceByCondition_NilSlice", nil, func(v int) bool { return v%2 == 0 }, nil},
|
||||
{"TestDropSliceByCondition_NonEmptySlice", []int{1, 2, 3, 4, 5}, func(v int) bool { return v%2 == 1 }, []int{2, 4}},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
collection.DropSliceByCondition(&c.input, c.condition)
|
||||
if len(c.input) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the length of input is not equal")
|
||||
}
|
||||
for i := 0; i < len(c.input); i++ {
|
||||
if c.input[i] != c.expected[i] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the inputV of input is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropSliceOverlappingElements(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
anotherSlice []int
|
||||
comparisonHandler collection.ComparisonHandler[int]
|
||||
expected []int
|
||||
expectedAnother []int
|
||||
expectedComparison []int
|
||||
}{
|
||||
{"TestDropSliceOverlappingElements_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{3, 4, 5, 6, 7}, func(v1, v2 int) bool { return v1 == v2 }, []int{1, 2}, []int{6, 7}, []int{3, 4, 5}},
|
||||
{"TestDropSliceOverlappingElements_EmptySlice", []int{}, []int{3, 4, 5, 6, 7}, func(v1, v2 int) bool { return v1 == v2 }, []int{}, []int{3, 4, 5, 6, 7}, []int{}},
|
||||
{"TestDropSliceOverlappingElements_NilSlice", nil, []int{3, 4, 5, 6, 7}, func(v1, v2 int) bool { return v1 == v2 }, nil, []int{3, 4, 5, 6, 7}, nil},
|
||||
{"TestDropSliceOverlappingElements_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{}, func(v1, v2 int) bool { return v1 == v2 }, []int{1, 2, 3, 4, 5}, []int{}, []int{}},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
collection.DropSliceOverlappingElements(&c.input, c.anotherSlice, c.comparisonHandler)
|
||||
if len(c.input) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the length of input is not equal")
|
||||
}
|
||||
for i := 0; i < len(c.input); i++ {
|
||||
if c.input[i] != c.expected[i] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the inputV of input is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package sher
|
||||
package collection
|
||||
|
||||
// DeduplicateSliceInPlace 去除切片中的重复元素
|
||||
func DeduplicateSliceInPlace[S ~[]V, V comparable](s *S) {
|
|
@ -0,0 +1,37 @@
|
|||
package collection
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleDeduplicateSliceInPlace() {
|
||||
slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1}
|
||||
DeduplicateSliceInPlace(&slice)
|
||||
fmt.Println(slice)
|
||||
// Output:
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
|
||||
func ExampleDeduplicateSlice() {
|
||||
slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1}
|
||||
fmt.Println(DeduplicateSlice(slice))
|
||||
// Output:
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
|
||||
func ExampleDeduplicateSliceInPlaceWithCompare() {
|
||||
slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1}
|
||||
DeduplicateSliceInPlaceWithCompare(&slice, func(a, b int) bool {
|
||||
return a == b
|
||||
})
|
||||
fmt.Println(slice)
|
||||
// Output:
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
|
||||
func ExampleDeduplicateSliceWithCompare() {
|
||||
slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1}
|
||||
fmt.Println(DeduplicateSliceWithCompare(slice, func(a, b int) bool {
|
||||
return a == b
|
||||
}))
|
||||
// Output:
|
||||
// [1 2 3 4 5]
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDeduplicateSliceInPlace(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected []int
|
||||
}{
|
||||
{name: "TestDeduplicateSliceInPlace_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}},
|
||||
{name: "TestDeduplicateSliceInPlace_Empty", input: []int{}, expected: []int{}},
|
||||
{name: "TestDeduplicateSliceInPlace_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
collection.DeduplicateSliceInPlace(&c.input)
|
||||
if len(c.input) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||
}
|
||||
for i := 0; i < len(c.input); i++ {
|
||||
av, ev := c.input[i], c.expected[i]
|
||||
if av != ev {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeduplicateSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected []int
|
||||
}{
|
||||
{name: "TestDeduplicateSlice_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}},
|
||||
{name: "TestDeduplicateSlice_Empty", input: []int{}, expected: []int{}},
|
||||
{name: "TestDeduplicateSlice_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.DeduplicateSlice(c.input)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
for i := 0; i < len(actual); i++ {
|
||||
av, ev := actual[i], c.expected[i]
|
||||
if av != ev {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeduplicateSliceInPlaceWithCompare(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected []int
|
||||
}{
|
||||
{name: "TestDeduplicateSliceInPlaceWithCompare_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}},
|
||||
{name: "TestDeduplicateSliceInPlaceWithCompare_Empty", input: []int{}, expected: []int{}},
|
||||
{name: "TestDeduplicateSliceInPlaceWithCompare_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
collection.DeduplicateSliceInPlaceWithCompare(&c.input, func(a, b int) bool {
|
||||
return a == b
|
||||
})
|
||||
if len(c.input) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||
}
|
||||
for i := 0; i < len(c.input); i++ {
|
||||
av, ev := c.input[i], c.expected[i]
|
||||
if av != ev {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeduplicateSliceWithCompare(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected []int
|
||||
}{
|
||||
{name: "TestDeduplicateSliceWithCompare_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}},
|
||||
{name: "TestDeduplicateSliceWithCompare_Empty", input: []int{}, expected: []int{}},
|
||||
{name: "TestDeduplicateSliceWithCompare_Nil", input: nil, expected: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.DeduplicateSliceWithCompare(c.input, func(a, b int) bool {
|
||||
return a == b
|
||||
})
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
for i := 0; i < len(actual); i++ {
|
||||
av, ev := actual[i], c.expected[i]
|
||||
if av != ev {
|
||||
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,18 +1,21 @@
|
|||
package sher
|
||||
package collection
|
||||
|
||||
// FilterOutByIndices 过滤切片中特定索引的元素,返回过滤后的切片
|
||||
func FilterOutByIndices[S ~[]V, V any](slice S, indices ...int) S {
|
||||
if slice == nil {
|
||||
return nil
|
||||
}
|
||||
if len(indices) == 0 {
|
||||
func FilterOutByIndices[S []V, V any](slice S, indices ...int) S {
|
||||
if slice == nil || len(slice) == 0 || len(indices) == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
excludeMap := make(map[int]bool)
|
||||
for _, ex := range indices {
|
||||
if ex >= 0 && ex < len(slice) {
|
||||
excludeMap[ex] = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(excludeMap) == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
validElements := make([]V, 0, len(slice)-len(excludeMap))
|
||||
for i, v := range slice {
|
||||
|
@ -97,7 +100,7 @@ func FilterOutByKeys[M ~map[K]V, K comparable, V any](m M, keys ...K) M {
|
|||
return validMap
|
||||
}
|
||||
|
||||
// FilterOutByValues 过滤 map 中多个 value,返回过滤后的 map
|
||||
// FilterOutByValues 过滤 map 中多个 values,返回过滤后的 map
|
||||
func FilterOutByValues[M ~map[K]V, K comparable, V any](m M, values []V, handler ComparisonHandler[V]) M {
|
||||
if m == nil {
|
||||
return nil
|
||||
|
@ -128,7 +131,7 @@ func FilterOutByMap[M ~map[K]V, K comparable, V any](m M, condition func(k K, v
|
|||
|
||||
validMap := make(M, len(m))
|
||||
for k, v := range m {
|
||||
if condition(k, v) {
|
||||
if !condition(k, v) {
|
||||
validMap[k] = v
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
)
|
||||
|
||||
func ExampleFilterOutByIndices() {
|
||||
var slice = []int{1, 2, 3, 4, 5}
|
||||
var result = collection.FilterOutByIndices(slice, 1, 3)
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// [1 3 5]
|
||||
}
|
||||
|
||||
func ExampleFilterOutByCondition() {
|
||||
var slice = []int{1, 2, 3, 4, 5}
|
||||
var result = collection.FilterOutByCondition(slice, func(v int) bool {
|
||||
return v%2 == 0
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// [1 3 5]
|
||||
}
|
||||
|
||||
func ExampleFilterOutByKey() {
|
||||
var m = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
var result = collection.FilterOutByKey(m, "b")
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// map[a:1 c:3]
|
||||
}
|
||||
|
||||
func ExampleFilterOutByValue() {
|
||||
var m = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
var result = collection.FilterOutByValue(m, 2, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
fmt.Println(len(result))
|
||||
// Output:
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleFilterOutByKeys() {
|
||||
var m = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
var result = collection.FilterOutByKeys(m, "a", "c")
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// map[b:2]
|
||||
}
|
||||
|
||||
func ExampleFilterOutByValues() {
|
||||
var m = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
var result = collection.FilterOutByValues(m, []int{1}, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
for i, s := range []string{"a", "b", "c"} {
|
||||
fmt.Println(i, result[s])
|
||||
}
|
||||
// Output:
|
||||
// 0 0
|
||||
// 1 2
|
||||
// 2 3
|
||||
}
|
||||
|
||||
func ExampleFilterOutByMap() {
|
||||
var m = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
var result = collection.FilterOutByMap(m, func(k string, v int) bool {
|
||||
return k == "a" || v == 3
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// map[b:2]
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFilterOutByIndices(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
indices []int
|
||||
expected []int
|
||||
}{
|
||||
{"TestFilterOutByIndices_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{1, 3}, []int{1, 3, 5}},
|
||||
{"TestFilterOutByIndices_EmptySlice", []int{}, []int{1, 3}, []int{}},
|
||||
{"TestFilterOutByIndices_NilSlice", nil, []int{1, 3}, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FilterOutByIndices(c.input, c.indices...)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of input is not equal")
|
||||
}
|
||||
for i := 0; i < len(actual); i++ {
|
||||
if actual[i] != c.expected[i] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of input is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOutByCondition(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
condition func(int) bool
|
||||
expected []int
|
||||
}{
|
||||
{"TestFilterOutByCondition_NonEmptySlice", []int{1, 2, 3, 4, 5}, func(v int) bool {
|
||||
return v%2 == 0
|
||||
}, []int{1, 3, 5}},
|
||||
{"TestFilterOutByCondition_EmptySlice", []int{}, func(v int) bool {
|
||||
return v%2 == 0
|
||||
}, []int{}},
|
||||
{"TestFilterOutByCondition_NilSlice", nil, func(v int) bool {
|
||||
return v%2 == 0
|
||||
}, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FilterOutByCondition(c.input, c.condition)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of input is not equal")
|
||||
}
|
||||
for i := 0; i < len(actual); i++ {
|
||||
if actual[i] != c.expected[i] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of input is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOutByKey(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
key int
|
||||
expected map[int]int
|
||||
}{
|
||||
{"TestFilterOutByKey_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 1, map[int]int{2: 2, 3: 3}},
|
||||
{"TestFilterOutByKey_EmptyMap", map[int]int{}, 1, map[int]int{}},
|
||||
{"TestFilterOutByKey_NilMap", nil, 1, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FilterOutByKey(c.input, c.key)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal")
|
||||
}
|
||||
for k, v := range actual {
|
||||
if v != c.expected[k] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOutByValue(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
value int
|
||||
expected map[int]int
|
||||
}{
|
||||
{"TestFilterOutByValue_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 1, map[int]int{2: 2, 3: 3}},
|
||||
{"TestFilterOutByValue_EmptyMap", map[int]int{}, 1, map[int]int{}},
|
||||
{"TestFilterOutByValue_NilMap", nil, 1, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FilterOutByValue(c.input, c.value, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal")
|
||||
}
|
||||
for k, v := range actual {
|
||||
if v != c.expected[k] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOutByKeys(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
keys []int
|
||||
expected map[int]int
|
||||
}{
|
||||
{"TestFilterOutByKeys_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, []int{1, 3}, map[int]int{2: 2}},
|
||||
{"TestFilterOutByKeys_EmptyMap", map[int]int{}, []int{1, 3}, map[int]int{}},
|
||||
{"TestFilterOutByKeys_NilMap", nil, []int{1, 3}, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FilterOutByKeys(c.input, c.keys...)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal")
|
||||
}
|
||||
for k, v := range actual {
|
||||
if v != c.expected[k] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOutByValues(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
values []int
|
||||
expected map[int]int
|
||||
}{
|
||||
{"TestFilterOutByValues_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, []int{1, 3}, map[int]int{2: 2}},
|
||||
{"TestFilterOutByValues_EmptyMap", map[int]int{}, []int{1, 3}, map[int]int{}},
|
||||
{"TestFilterOutByValues_NilMap", nil, []int{1, 3}, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FilterOutByValues(c.input, c.values, func(source, target int) bool {
|
||||
return source == target
|
||||
})
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal")
|
||||
}
|
||||
for k, v := range actual {
|
||||
if v != c.expected[k] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOutByMap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input map[int]int
|
||||
filter map[int]int
|
||||
expected map[int]int
|
||||
}{
|
||||
{"TestFilterOutByMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{1: 1, 3: 3}, map[int]int{2: 2}},
|
||||
{"TestFilterOutByMap_EmptyMap", map[int]int{}, map[int]int{1: 1, 3: 3}, map[int]int{}},
|
||||
{"TestFilterOutByMap_NilMap", nil, map[int]int{1: 1, 3: 3}, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FilterOutByMap(c.input, func(k int, v int) bool {
|
||||
return c.filter[k] == v
|
||||
})
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal")
|
||||
}
|
||||
for k, v := range actual {
|
||||
if v != c.expected[k] {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
package collection
|
||||
|
||||
import "github.com/kercylan98/minotaur/utils/generic"
|
||||
|
||||
// FindLoopedNextInSlice 返回 i 的下一个数组成员,当 i 达到数组长度时从 0 开始
|
||||
// - 当 i 为负数时将返回第一个元素
|
||||
func FindLoopedNextInSlice[S ~[]V, V any](slice S, i int) (next int, value V) {
|
||||
if i < 0 {
|
||||
return 0, slice[0]
|
||||
}
|
||||
next = i + 1
|
||||
if next == len(slice) {
|
||||
next = 0
|
||||
}
|
||||
return next, slice[next]
|
||||
}
|
||||
|
||||
// FindLoopedPrevInSlice 返回 i 的上一个数组成员,当 i 为 0 时从数组末尾开始
|
||||
// - 当 i 为负数时将返回最后一个元素
|
||||
func FindLoopedPrevInSlice[S ~[]V, V any](slice S, i int) (prev int, value V) {
|
||||
if i < 0 {
|
||||
return len(slice) - 1, slice[len(slice)-1]
|
||||
}
|
||||
prev = i - 1
|
||||
if prev == -1 {
|
||||
prev = len(slice) - 1
|
||||
}
|
||||
return prev, slice[prev]
|
||||
}
|
||||
|
||||
// FindCombinationsInSliceByRange 获取给定数组的所有组合,且每个组合的成员数量限制在指定范围内
|
||||
func FindCombinationsInSliceByRange[S ~[]V, V any](s S, minSize, maxSize int) []S {
|
||||
n := len(s)
|
||||
if n == 0 || minSize <= 0 || maxSize <= 0 || minSize > maxSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result []S
|
||||
var currentCombination S
|
||||
|
||||
var backtrack func(startIndex int, currentSize int)
|
||||
backtrack = func(startIndex int, currentSize int) {
|
||||
if currentSize >= minSize && currentSize <= maxSize {
|
||||
combination := make(S, len(currentCombination))
|
||||
copy(combination, currentCombination)
|
||||
result = append(result, combination)
|
||||
}
|
||||
|
||||
for i := startIndex; i < n; i++ {
|
||||
currentCombination = append(currentCombination, s[i])
|
||||
backtrack(i+1, currentSize+1)
|
||||
currentCombination = currentCombination[:len(currentCombination)-1]
|
||||
}
|
||||
}
|
||||
|
||||
backtrack(0, 0)
|
||||
return result
|
||||
}
|
||||
|
||||
// FindFirstOrDefaultInSlice 判断切片中是否存在元素,返回第一个元素,不存在则返回默认值
|
||||
func FindFirstOrDefaultInSlice[S ~[]V, V any](slice S, defaultValue V) V {
|
||||
if len(slice) == 0 {
|
||||
return defaultValue
|
||||
}
|
||||
return slice[0]
|
||||
}
|
||||
|
||||
// FindOrDefaultInSlice 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则返回默认值
|
||||
func FindOrDefaultInSlice[S ~[]V, V any](slice S, defaultValue V, handler func(v V) bool) (t V) {
|
||||
if len(slice) == 0 {
|
||||
return defaultValue
|
||||
}
|
||||
for _, v := range slice {
|
||||
if handler(v) {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// FindOrDefaultInComparableSlice 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则返回默认值
|
||||
func FindOrDefaultInComparableSlice[S ~[]V, V comparable](slice S, v V, defaultValue V) (t V) {
|
||||
if len(slice) == 0 {
|
||||
return defaultValue
|
||||
}
|
||||
for _, value := range slice {
|
||||
if value == v {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// FindInSlice 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1
|
||||
func FindInSlice[S ~[]V, V any](slice S, handler func(v V) bool) (i int, t V) {
|
||||
if len(slice) == 0 {
|
||||
return -1, t
|
||||
}
|
||||
for i, v := range slice {
|
||||
if handler(v) {
|
||||
return i, v
|
||||
}
|
||||
}
|
||||
return -1, t
|
||||
}
|
||||
|
||||
// FindIndexInSlice 判断切片中是否存在某个元素,返回第一个匹配的索引,不存在则索引返回 -1
|
||||
func FindIndexInSlice[S ~[]V, V any](slice S, handler func(v V) bool) int {
|
||||
if len(slice) == 0 {
|
||||
return -1
|
||||
}
|
||||
for i, v := range slice {
|
||||
if handler(v) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// FindInComparableSlice 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1
|
||||
func FindInComparableSlice[S ~[]V, V comparable](slice S, v V) (i int, t V) {
|
||||
if len(slice) == 0 {
|
||||
return -1, t
|
||||
}
|
||||
for i, value := range slice {
|
||||
if value == v {
|
||||
return i, value
|
||||
}
|
||||
}
|
||||
return -1, t
|
||||
}
|
||||
|
||||
// FindIndexInComparableSlice 判断切片中是否存在某个元素,返回第一个匹配的索引,不存在则索引返回 -1
|
||||
func FindIndexInComparableSlice[S ~[]V, V comparable](slice S, v V) int {
|
||||
if len(slice) == 0 {
|
||||
return -1
|
||||
}
|
||||
for i, value := range slice {
|
||||
if value == v {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// FindInSliceByBinary 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1
|
||||
func FindInSliceByBinary[S ~[]V, V any](slice S, handler func(v V) bool) (i int, t V) {
|
||||
low := 0
|
||||
high := len(slice) - 1
|
||||
|
||||
for low <= high {
|
||||
mid := low + (high-low)/2
|
||||
if handler(slice[mid]) {
|
||||
return mid, slice[mid]
|
||||
} else if handler(slice[mid]) {
|
||||
high = mid - 1
|
||||
} else {
|
||||
low = mid + 1
|
||||
}
|
||||
}
|
||||
return -1, t
|
||||
}
|
||||
|
||||
// FindMinimumInSlice 获取切片中的最小值
|
||||
func FindMinimumInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (result V) {
|
||||
if len(slice) == 0 {
|
||||
return
|
||||
}
|
||||
result = slice[0]
|
||||
for i := 1; i < len(slice); i++ {
|
||||
if handler(slice[i], result) {
|
||||
result = slice[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindMaximumInSlice 获取切片中的最大值
|
||||
func FindMaximumInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (result V) {
|
||||
if len(slice) == 0 {
|
||||
return
|
||||
}
|
||||
result = slice[0]
|
||||
for i := 1; i < len(slice); i++ {
|
||||
if handler(result, slice[i]) {
|
||||
result = slice[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindMin2MaxInSlice 获取切片中的最小值和最大值
|
||||
func FindMin2MaxInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (min, max V) {
|
||||
if len(slice) == 0 {
|
||||
return
|
||||
}
|
||||
min = slice[0]
|
||||
max = slice[0]
|
||||
for i := 1; i < len(slice); i++ {
|
||||
if handler(slice[i], min) {
|
||||
min = slice[i]
|
||||
}
|
||||
if handler(max, slice[i]) {
|
||||
max = slice[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindMinFromMap 获取 map 中的最小值
|
||||
func FindMinFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (result V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
for _, v := range m {
|
||||
if handler(v, result) {
|
||||
result = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindMaxFromMap 获取 map 中的最大值
|
||||
func FindMaxFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (result V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
for _, v := range m {
|
||||
if handler(result, v) {
|
||||
result = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindMin2MaxFromMap 获取 map 中的最小值和最大值
|
||||
func FindMin2MaxFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (min, max V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
for _, v := range m {
|
||||
if handler(v, min) {
|
||||
min = v
|
||||
}
|
||||
if handler(max, v) {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindInMapKey 判断 map 中是否存在某个 key
|
||||
func FindInMapKey[M ~map[K]V, K comparable, V any](m M, k K) bool {
|
||||
_, exist := m[k]
|
||||
return exist
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
)
|
||||
|
||||
func ExampleFindLoopedNextInSlice() {
|
||||
next, v := collection.FindLoopedNextInSlice([]int{1, 2, 3}, 1)
|
||||
fmt.Println(next, v)
|
||||
// Output:
|
||||
// 2 3
|
||||
}
|
||||
|
||||
func ExampleFindLoopedPrevInSlice() {
|
||||
prev, v := collection.FindLoopedPrevInSlice([]int{1, 2, 3}, 1)
|
||||
fmt.Println(prev, v)
|
||||
// Output:
|
||||
// 0 1
|
||||
}
|
||||
|
||||
func ExampleFindCombinationsInSliceByRange() {
|
||||
result := collection.FindCombinationsInSliceByRange([]int{1, 2, 3}, 1, 2)
|
||||
fmt.Println(len(result))
|
||||
// Output:
|
||||
// 6
|
||||
}
|
||||
|
||||
func ExampleFindFirstOrDefaultInSlice() {
|
||||
result := collection.FindFirstOrDefaultInSlice([]int{1, 2, 3}, 0)
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleFindOrDefaultInSlice() {
|
||||
result := collection.FindOrDefaultInSlice([]int{1, 2, 3}, 0, func(v int) bool {
|
||||
return v == 2
|
||||
})
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleFindOrDefaultInComparableSlice() {
|
||||
result := collection.FindOrDefaultInComparableSlice([]int{1, 2, 3}, 2, 0)
|
||||
fmt.Println(result)
|
||||
// Output:
|
||||
// 2
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package collection_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFindLoopedNextInSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
i int
|
||||
expected int
|
||||
}{
|
||||
{"TestFindLoopedNextInSlice_NonEmptySlice", []int{1, 2, 3}, 1, 2},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual, _ := collection.FindLoopedNextInSlice(c.input, c.i)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the next index of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindLoopedPrevInSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
i int
|
||||
expected int
|
||||
}{
|
||||
{"TestFindLoopedPrevInSlice_NonEmptySlice", []int{1, 2, 3}, 1, 0},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual, _ := collection.FindLoopedPrevInSlice(c.input, c.i)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the prev index of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindCombinationsInSliceByRange(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
minSize int
|
||||
maxSize int
|
||||
expected [][]int
|
||||
}{
|
||||
{"TestFindCombinationsInSliceByRange_NonEmptySlice", []int{1, 2, 3}, 1, 2, [][]int{{1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}}},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FindCombinationsInSliceByRange(c.input, c.minSize, c.maxSize)
|
||||
if len(actual) != len(c.expected) {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the length of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindFirstOrDefaultInSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected int
|
||||
}{
|
||||
{"TestFindFirstOrDefaultInSlice_NonEmptySlice", []int{1, 2, 3}, 1},
|
||||
{"TestFindFirstOrDefaultInSlice_EmptySlice", []int{}, 0},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FindFirstOrDefaultInSlice(c.input, 0)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the first element of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindOrDefaultInSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected int
|
||||
}{
|
||||
{"TestFindOrDefaultInSlice_NonEmptySlice", []int{1, 2, 3}, 2},
|
||||
{"TestFindOrDefaultInSlice_EmptySlice", []int{}, 0},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FindOrDefaultInSlice(c.input, 0, func(v int) bool {
|
||||
return v == 2
|
||||
})
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the element of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindOrDefaultInComparableSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
input []int
|
||||
expected int
|
||||
}{
|
||||
{"TestFindOrDefaultInComparableSlice_NonEmptySlice", []int{1, 2, 3}, 2},
|
||||
{"TestFindOrDefaultInComparableSlice_EmptySlice", []int{}, 0},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
actual := collection.FindOrDefaultInComparableSlice(c.input, 2, 0)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the element of input is not equal")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package collection
|
||||
|
||||
// SwapSlice 将切片中的两个元素进行交换
|
||||
func SwapSlice[S ~[]V, V any](slice *S, i, j int) {
|
||||
(*slice)[i], (*slice)[j] = (*slice)[j], (*slice)[i]
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package listings
|
||||
|
||||
// NewMatrix 创建一个新的 Matrix 实例。
|
||||
func NewMatrix[V any](dimensions ...int) *Matrix[V] {
|
||||
total := 1
|
||||
for _, dim := range dimensions {
|
||||
total *= dim
|
||||
}
|
||||
return &Matrix[V]{
|
||||
dimensions: dimensions,
|
||||
data: make([]V, total),
|
||||
}
|
||||
}
|
||||
|
||||
type Matrix[V any] struct {
|
||||
dimensions []int // 维度大小的切片
|
||||
data []V // 存储矩阵数据的一维切片
|
||||
}
|
||||
|
||||
// Get 获取矩阵中给定索引的元素。
|
||||
func (slf *Matrix[V]) Get(index ...int) *V {
|
||||
if len(index) != len(slf.dimensions) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var offset = 0
|
||||
for i, dim := range slf.dimensions {
|
||||
if index[i] < 0 || index[i] >= dim {
|
||||
return nil
|
||||
}
|
||||
offset = offset*dim + index[i]
|
||||
}
|
||||
return &slf.data[offset]
|
||||
}
|
||||
|
||||
// Set 设置矩阵中给定索引的元素。
|
||||
func (slf *Matrix[V]) Set(index []int, value V) {
|
||||
if len(index) != len(slf.dimensions) {
|
||||
return
|
||||
}
|
||||
|
||||
var offset = 0
|
||||
for i, dim := range slf.dimensions {
|
||||
if index[i] < 0 || index[i] >= dim {
|
||||
return
|
||||
}
|
||||
offset = offset*dim + index[i]
|
||||
}
|
||||
slf.data[offset] = value
|
||||
}
|
||||
|
||||
// Dimensions 返回矩阵的维度大小。
|
||||
func (slf *Matrix[V]) Dimensions() []int {
|
||||
return slf.dimensions
|
||||
}
|
||||
|
||||
// Clear 清空矩阵。
|
||||
func (slf *Matrix[V]) Clear() {
|
||||
slf.data = make([]V, len(slf.data))
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package slice
|
||||
package listings
|
||||
|
||||
// NewPagedSlice 创建一个新的 PagedSlice 实例。
|
||||
func NewPagedSlice[T any](pageSize int) *PagedSlice[T] {
|
|
@ -0,0 +1,198 @@
|
|||
package listings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// NewPrioritySlice 创建一个优先级切片
|
||||
func NewPrioritySlice[V any](lengthAndCap ...int) *PrioritySlice[V] {
|
||||
p := &PrioritySlice[V]{}
|
||||
if len(lengthAndCap) > 0 {
|
||||
var length = lengthAndCap[0]
|
||||
var c int
|
||||
if len(lengthAndCap) > 1 {
|
||||
c = lengthAndCap[1]
|
||||
}
|
||||
p.items = make([]*priorityItem[V], length, c)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// PrioritySlice 是一个优先级切片
|
||||
type PrioritySlice[V any] struct {
|
||||
items []*priorityItem[V]
|
||||
}
|
||||
|
||||
// Len 返回切片长度
|
||||
func (slf *PrioritySlice[V]) Len() int {
|
||||
return len(slf.items)
|
||||
}
|
||||
|
||||
// Cap 返回切片容量
|
||||
func (slf *PrioritySlice[V]) Cap() int {
|
||||
return cap(slf.items)
|
||||
}
|
||||
|
||||
// Clear 清空切片
|
||||
func (slf *PrioritySlice[V]) Clear() {
|
||||
slf.items = slf.items[:0]
|
||||
}
|
||||
|
||||
// Append 添加元素
|
||||
func (slf *PrioritySlice[V]) Append(v V, p int) {
|
||||
slf.items = append(slf.items, &priorityItem[V]{
|
||||
v: v,
|
||||
p: p,
|
||||
})
|
||||
slf.sort()
|
||||
}
|
||||
|
||||
// Appends 添加元素
|
||||
func (slf *PrioritySlice[V]) Appends(priority int, vs ...V) {
|
||||
for _, v := range vs {
|
||||
slf.Append(v, priority)
|
||||
}
|
||||
slf.sort()
|
||||
}
|
||||
|
||||
// Get 获取元素
|
||||
func (slf *PrioritySlice[V]) Get(index int) (V, int) {
|
||||
i := slf.items[index]
|
||||
return i.Value(), i.Priority()
|
||||
}
|
||||
|
||||
// GetValue 获取元素值
|
||||
func (slf *PrioritySlice[V]) GetValue(index int) V {
|
||||
return slf.items[index].Value()
|
||||
}
|
||||
|
||||
// GetPriority 获取元素优先级
|
||||
func (slf *PrioritySlice[V]) GetPriority(index int) int {
|
||||
return slf.items[index].Priority()
|
||||
}
|
||||
|
||||
// Set 设置元素
|
||||
func (slf *PrioritySlice[V]) Set(index int, value V, priority int) {
|
||||
before := slf.items[index]
|
||||
slf.items[index] = &priorityItem[V]{
|
||||
v: value,
|
||||
p: priority,
|
||||
}
|
||||
if before.Priority() != priority {
|
||||
slf.sort()
|
||||
}
|
||||
}
|
||||
|
||||
// SetValue 设置元素值
|
||||
func (slf *PrioritySlice[V]) SetValue(index int, value V) {
|
||||
slf.items[index].v = value
|
||||
}
|
||||
|
||||
// SetPriority 设置元素优先级
|
||||
func (slf *PrioritySlice[V]) SetPriority(index int, priority int) {
|
||||
slf.items[index].p = priority
|
||||
slf.sort()
|
||||
}
|
||||
|
||||
// Action 直接操作切片,如果返回值不为 nil,则替换切片
|
||||
func (slf *PrioritySlice[V]) Action(action func(items []*priorityItem[V]) []*priorityItem[V]) {
|
||||
if len(slf.items) == 0 {
|
||||
return
|
||||
}
|
||||
if replace := action(slf.items); replace != nil {
|
||||
slf.items = replace
|
||||
slf.sort()
|
||||
}
|
||||
}
|
||||
|
||||
// Range 遍历切片,如果返回值为 false,则停止遍历
|
||||
func (slf *PrioritySlice[V]) Range(action func(index int, item *priorityItem[V]) bool) {
|
||||
for i, item := range slf.items {
|
||||
if !action(i, item) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RangeValue 遍历切片值,如果返回值为 false,则停止遍历
|
||||
func (slf *PrioritySlice[V]) RangeValue(action func(index int, value V) bool) {
|
||||
slf.Range(func(index int, item *priorityItem[V]) bool {
|
||||
return action(index, item.Value())
|
||||
})
|
||||
}
|
||||
|
||||
// RangePriority 遍历切片优先级,如果返回值为 false,则停止遍历
|
||||
func (slf *PrioritySlice[V]) RangePriority(action func(index int, priority int) bool) {
|
||||
slf.Range(func(index int, item *priorityItem[V]) bool {
|
||||
return action(index, item.Priority())
|
||||
})
|
||||
}
|
||||
|
||||
// Slice 返回切片
|
||||
func (slf *PrioritySlice[V]) Slice() []V {
|
||||
var vs []V
|
||||
for _, item := range slf.items {
|
||||
vs = append(vs, item.Value())
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// String 返回切片字符串
|
||||
func (slf *PrioritySlice[V]) String() string {
|
||||
var vs []V
|
||||
for _, item := range slf.items {
|
||||
vs = append(vs, item.Value())
|
||||
}
|
||||
return fmt.Sprint(vs)
|
||||
}
|
||||
|
||||
// sort 排序
|
||||
func (slf *PrioritySlice[V]) sort() {
|
||||
if len(slf.items) <= 1 {
|
||||
return
|
||||
}
|
||||
sort.Slice(slf.items, func(i, j int) bool {
|
||||
return slf.items[i].Priority() < slf.items[j].Priority()
|
||||
})
|
||||
for i := 0; i < len(slf.items); i++ {
|
||||
if i == 0 {
|
||||
slf.items[i].prev = nil
|
||||
slf.items[i].next = slf.items[i+1]
|
||||
} else if i == len(slf.items)-1 {
|
||||
slf.items[i].prev = slf.items[i-1]
|
||||
slf.items[i].next = nil
|
||||
} else {
|
||||
slf.items[i].prev = slf.items[i-1]
|
||||
slf.items[i].next = slf.items[i+1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// priorityItem 是一个优先级切片元素
|
||||
type priorityItem[V any] struct {
|
||||
next *priorityItem[V]
|
||||
prev *priorityItem[V]
|
||||
v V
|
||||
p int
|
||||
}
|
||||
|
||||
// Value 返回元素值
|
||||
func (p *priorityItem[V]) Value() V {
|
||||
return p.v
|
||||
}
|
||||
|
||||
// Priority 返回元素优先级
|
||||
func (p *priorityItem[V]) Priority() int {
|
||||
return p.p
|
||||
}
|
||||
|
||||
// Next 返回下一个元素
|
||||
func (p *priorityItem[V]) Next() *priorityItem[V] {
|
||||
return p.next
|
||||
}
|
||||
|
||||
// Prev 返回上一个元素
|
||||
func (p *priorityItem[V]) Prev() *priorityItem[V] {
|
||||
return p.prev
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package listings_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/collection/listings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPrioritySlice_Append(t *testing.T) {
|
||||
var s = listings.NewPrioritySlice[string]()
|
||||
s.Append("name_1", 2)
|
||||
s.Append("name_2", 1)
|
||||
fmt.Println(s)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package sher
|
||||
package collection
|
||||
|
||||
// MappingFromSlice 将切片中的元素进行转换
|
||||
func MappingFromSlice[S ~[]V, NS ~[]N, V, N any](slice S, handler func(value V) N) NS {
|
|
@ -1,4 +1,4 @@
|
|||
package sher
|
||||
package collection
|
||||
|
||||
// MergeSlices 合并切片
|
||||
func MergeSlices[S ~[]V, V any](slices ...S) (result S) {
|
|
@ -1,18 +1,18 @@
|
|||
package sher
|
||||
package collection
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/random"
|
||||
)
|
||||
|
||||
// ChooseRandomSliceElementRepeatN 获取切片中的 n 个随机元素,允许重复
|
||||
// - 如果 n 大于切片长度或小于 0 时将会发生 panic
|
||||
// ChooseRandomSliceElementRepeatN 获取切片中的 inputN 个随机元素,允许重复
|
||||
// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic
|
||||
func ChooseRandomSliceElementRepeatN[S ~[]V, V any](slice S, n int) (result []V) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
if n > len(slice) || n < 0 {
|
||||
panic(fmt.Errorf("n is greater than the length of the slice or less than 0, n: %d, length: %d", n, len(slice)))
|
||||
panic(fmt.Errorf("inputN is greater than the length of the input or less than 0, inputN: %d, length: %d", n, len(slice)))
|
||||
}
|
||||
result = make([]V, n)
|
||||
for i := 0; i < n; i++ {
|
||||
|
@ -21,14 +21,14 @@ func ChooseRandomSliceElementRepeatN[S ~[]V, V any](slice S, n int) (result []V)
|
|||
return
|
||||
}
|
||||
|
||||
// ChooseRandomIndexRepeatN 获取切片中的 n 个随机元素的索引,允许重复
|
||||
// - 如果 n 大于切片长度或小于 0 时将会发生 panic
|
||||
// ChooseRandomIndexRepeatN 获取切片中的 inputN 个随机元素的索引,允许重复
|
||||
// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic
|
||||
func ChooseRandomIndexRepeatN[S ~[]V, V any](slice S, n int) (result []int) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
if n > len(slice) || n < 0 {
|
||||
panic(fmt.Errorf("n is greater than the length of the slice or less than 0, n: %d, length: %d", n, len(slice)))
|
||||
panic(fmt.Errorf("inputN is greater than the length of the input or less than 0, inputN: %d, length: %d", n, len(slice)))
|
||||
}
|
||||
result = make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
|
@ -53,14 +53,14 @@ func ChooseRandomIndex[S ~[]V, V any](slice S) (index int) {
|
|||
return random.Int(0, len(slice)-1)
|
||||
}
|
||||
|
||||
// ChooseRandomSliceElementN 获取切片中的 n 个随机元素
|
||||
// - 如果 n 大于切片长度或小于 0 时将会发生 panic
|
||||
// ChooseRandomSliceElementN 获取切片中的 inputN 个随机元素
|
||||
// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic
|
||||
func ChooseRandomSliceElementN[S ~[]V, V any](slice S, n int) (result []V) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
if n > len(slice) || n < 0 {
|
||||
panic(fmt.Errorf("n is greater than the length of the slice or less than 0, n: %d, length: %d", n, len(slice)))
|
||||
panic(fmt.Errorf("inputN is greater than the length of the input or less than 0, inputN: %d, length: %d", n, len(slice)))
|
||||
}
|
||||
result = make([]V, n)
|
||||
for i := 0; i < n; i++ {
|
||||
|
@ -69,14 +69,14 @@ func ChooseRandomSliceElementN[S ~[]V, V any](slice S, n int) (result []V) {
|
|||
return
|
||||
}
|
||||
|
||||
// ChooseRandomIndexN 获取切片中的 n 个随机元素的索引
|
||||
// - 如果 n 大于切片长度或小于 0 时将会发生 panic
|
||||
// ChooseRandomIndexN 获取切片中的 inputN 个随机元素的索引
|
||||
// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic
|
||||
func ChooseRandomIndexN[S ~[]V, V any](slice S, n int) (result []int) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
if n > len(slice) || n < 0 {
|
||||
panic(fmt.Errorf("n is greater than the length of the slice or less than 0, n: %d, length: %d", n, len(slice)))
|
||||
panic(fmt.Errorf("inputN is greater than the length of the input or less than 0, inputN: %d, length: %d", n, len(slice)))
|
||||
}
|
||||
result = make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
|
@ -85,14 +85,14 @@ func ChooseRandomIndexN[S ~[]V, V any](slice S, n int) (result []int) {
|
|||
return
|
||||
}
|
||||
|
||||
// ChooseRandomMapKeyRepeatN 获取 map 中的 n 个随机 key,允许重复
|
||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
||||
// ChooseRandomMapKeyRepeatN 获取 map 中的 inputN 个随机 key,允许重复
|
||||
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||
func ChooseRandomMapKeyRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (result []K) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
if n > len(m) || n < 0 {
|
||||
panic(fmt.Errorf("n is greater than the length of the map or less than 0, n: %d, length: %d", n, len(m)))
|
||||
panic(fmt.Errorf("inputN is greater than the length of the map or less than 0, inputN: %d, length: %d", n, len(m)))
|
||||
}
|
||||
result = make([]K, n)
|
||||
for i := 0; i < n; i++ {
|
||||
|
@ -104,14 +104,14 @@ func ChooseRandomMapKeyRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (res
|
|||
return
|
||||
}
|
||||
|
||||
// ChooseRandomMapValueRepeatN 获取 map 中的 n 个随机 value,允许重复
|
||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
||||
// ChooseRandomMapValueRepeatN 获取 map 中的 inputN 个随机 inputV,允许重复
|
||||
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||
func ChooseRandomMapValueRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (result []V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
if n > len(m) || n < 0 {
|
||||
panic(fmt.Errorf("n is greater than the length of the map or less than 0, n: %d, length: %d", n, len(m)))
|
||||
panic(fmt.Errorf("inputN is greater than the length of the map or less than 0, inputN: %d, length: %d", n, len(m)))
|
||||
}
|
||||
result = make([]V, n)
|
||||
for i := 0; i < n; i++ {
|
||||
|
@ -123,14 +123,14 @@ func ChooseRandomMapValueRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (r
|
|||
return
|
||||
}
|
||||
|
||||
// ChooseRandomMapKeyAndValueRepeatN 获取 map 中的 n 个随机 key 和 value,允许重复
|
||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
||||
// ChooseRandomMapKeyAndValueRepeatN 获取 map 中的 inputN 个随机 key 和 inputV,允许重复
|
||||
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||
func ChooseRandomMapKeyAndValueRepeatN[M ~map[K]V, K comparable, V any](m M, n int) M {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
if n > len(m) || n < 0 {
|
||||
panic(fmt.Errorf("n is greater than the length of the map or less than 0, n: %d, length: %d", n, len(m)))
|
||||
panic(fmt.Errorf("inputN is greater than the length of the map or less than 0, inputN: %d, length: %d", n, len(m)))
|
||||
}
|
||||
result := make(M, n)
|
||||
for i := 0; i < n; i++ {
|
||||
|
@ -153,7 +153,7 @@ func ChooseRandomMapKey[M ~map[K]V, K comparable, V any](m M) (k K) {
|
|||
return
|
||||
}
|
||||
|
||||
// ChooseRandomMapValue 获取 map 中的随机 value
|
||||
// ChooseRandomMapValue 获取 map 中的随机 inputV
|
||||
func ChooseRandomMapValue[M ~map[K]V, K comparable, V any](m M) (v V) {
|
||||
if m == nil {
|
||||
return
|
||||
|
@ -164,14 +164,14 @@ func ChooseRandomMapValue[M ~map[K]V, K comparable, V any](m M) (v V) {
|
|||
return
|
||||
}
|
||||
|
||||
// ChooseRandomMapKeyN 获取 map 中的 n 个随机 key
|
||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
||||
// ChooseRandomMapKeyN 获取 map 中的 inputN 个随机 key
|
||||
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||
func ChooseRandomMapKeyN[M ~map[K]V, K comparable, V any](m M, n int) (result []K) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
if n > len(m) || n < 0 {
|
||||
panic(fmt.Errorf("n is greater than the length of the map or less than 0, n: %d, length: %d", n, len(m)))
|
||||
panic(fmt.Errorf("inputN is greater than the length of the map or less than 0, inputN: %d, length: %d", n, len(m)))
|
||||
}
|
||||
result = make([]K, n)
|
||||
i := 0
|
||||
|
@ -185,14 +185,14 @@ func ChooseRandomMapKeyN[M ~map[K]V, K comparable, V any](m M, n int) (result []
|
|||
return
|
||||
}
|
||||
|
||||
// ChooseRandomMapValueN 获取 map 中的 n 个随机 value
|
||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
||||
// ChooseRandomMapValueN 获取 map 中的 inputN 个随机 inputV
|
||||
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||
func ChooseRandomMapValueN[M ~map[K]V, K comparable, V any](m M, n int) (result []V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
if n > len(m) || n < 0 {
|
||||
panic(fmt.Errorf("n is greater than the length of the map or less than 0, n: %d, length: %d", n, len(m)))
|
||||
panic(fmt.Errorf("inputN is greater than the length of the map or less than 0, inputN: %d, length: %d", n, len(m)))
|
||||
}
|
||||
result = make([]V, n)
|
||||
i := 0
|
||||
|
@ -206,7 +206,7 @@ func ChooseRandomMapValueN[M ~map[K]V, K comparable, V any](m M, n int) (result
|
|||
return
|
||||
}
|
||||
|
||||
// ChooseRandomMapKeyAndValue 获取 map 中的随机 key 和 value
|
||||
// ChooseRandomMapKeyAndValue 获取 map 中的随机 key 和 inputV
|
||||
func ChooseRandomMapKeyAndValue[M ~map[K]V, K comparable, V any](m M) (k K, v V) {
|
||||
if m == nil {
|
||||
return
|
||||
|
@ -217,14 +217,14 @@ func ChooseRandomMapKeyAndValue[M ~map[K]V, K comparable, V any](m M) (k K, v V)
|
|||
return
|
||||
}
|
||||
|
||||
// ChooseRandomMapKeyAndValueN 获取 map 中的 n 个随机 key 和 value
|
||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
||||
// ChooseRandomMapKeyAndValueN 获取 map 中的 inputN 个随机 key 和 inputV
|
||||
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||
func ChooseRandomMapKeyAndValueN[M ~map[K]V, K comparable, V any](m M, n int) M {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
if n > len(m) || n < 0 {
|
||||
panic(fmt.Errorf("n is greater than the length of the map or less than 0, n: %d, length: %d", n, len(m)))
|
||||
panic(fmt.Errorf("inputN is greater than the length of the map or less than 0, inputN: %d, length: %d", n, len(m)))
|
||||
}
|
||||
result := make(M, n)
|
||||
i := 0
|
|
@ -1,4 +1,4 @@
|
|||
package sher
|
||||
package collection
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
|
@ -6,6 +6,16 @@ import (
|
|||
"sort"
|
||||
)
|
||||
|
||||
// DescBy 返回降序比较结果
|
||||
func DescBy[Sort generic.Ordered](a, b Sort) bool {
|
||||
return a > b
|
||||
}
|
||||
|
||||
// AscBy 返回升序比较结果
|
||||
func AscBy[Sort generic.Ordered](a, b Sort) bool {
|
||||
return a < b
|
||||
}
|
||||
|
||||
// Desc 对切片进行降序排序
|
||||
func Desc[S ~[]V, V any, Sort generic.Ordered](slice *S, getter func(index int) Sort) {
|
||||
sort.Slice(*slice, func(i, j int) bool {
|
|
@ -1,8 +1,8 @@
|
|||
package combination
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
@ -24,7 +24,7 @@ func WithMatcherEvaluation[T Item](evaluate func(items []T) float64) MatcherOpti
|
|||
func WithMatcherLeastLength[T Item](length int) MatcherOption[T] {
|
||||
return func(m *Matcher[T]) {
|
||||
m.AddFilter(func(items []T) [][]T {
|
||||
return slice.LimitedCombinations(items, length, len(items))
|
||||
return collection.FindCombinationsInSliceByRange(items, length, len(items))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func WithMatcherLeastLength[T Item](length int) MatcherOption[T] {
|
|||
func WithMatcherLength[T Item](length int) MatcherOption[T] {
|
||||
return func(m *Matcher[T]) {
|
||||
m.AddFilter(func(items []T) [][]T {
|
||||
return slice.LimitedCombinations(items, length, length)
|
||||
return collection.FindCombinationsInSliceByRange(items, length, length)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ func WithMatcherLength[T Item](length int) MatcherOption[T] {
|
|||
func WithMatcherMostLength[T Item](length int) MatcherOption[T] {
|
||||
return func(m *Matcher[T]) {
|
||||
m.AddFilter(func(items []T) [][]T {
|
||||
return slice.LimitedCombinations(items, 1, length)
|
||||
return collection.FindCombinationsInSliceByRange(items, 1, length)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func WithMatcherMostLength[T Item](length int) MatcherOption[T] {
|
|||
func WithMatcherIntervalLength[T Item](min, max int) MatcherOption[T] {
|
||||
return func(m *Matcher[T]) {
|
||||
m.AddFilter(func(items []T) [][]T {
|
||||
return slice.LimitedCombinations(items, min, max)
|
||||
return collection.FindCombinationsInSliceByRange(items, min, max)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ func WithMatcherSame[T Item, E generic.Ordered](count int, getType func(item T)
|
|||
return func(m *Matcher[T]) {
|
||||
m.AddFilter(func(items []T) [][]T {
|
||||
var combinations [][]T
|
||||
groups := slice.LimitedCombinations(items, count, count)
|
||||
groups := collection.FindCombinationsInSliceByRange(items, count, count)
|
||||
for _, items := range groups {
|
||||
if count > 0 && len(items) != count {
|
||||
continue
|
||||
|
@ -147,8 +147,12 @@ func WithMatcherNCarryM[T Item, E generic.Ordered](n, m int, getType func(item T
|
|||
if len(group) != n {
|
||||
continue
|
||||
}
|
||||
other := slice.SubWithCheck(items, group, func(a, b T) bool { return reflect.DeepEqual(a, b) })
|
||||
ms := slice.LimitedCombinations(other, m, m)
|
||||
|
||||
other := collection.CloneSlice(items)
|
||||
collection.DropSliceOverlappingElements(&other, group, func(source, target T) bool {
|
||||
return reflect.DeepEqual(source, target)
|
||||
})
|
||||
ms := collection.FindCombinationsInSliceByRange(other, m, m)
|
||||
for _, otherGroup := range ms {
|
||||
var t E
|
||||
var init = true
|
||||
|
@ -163,7 +167,7 @@ func WithMatcherNCarryM[T Item, E generic.Ordered](n, m int, getType func(item T
|
|||
}
|
||||
}
|
||||
if same {
|
||||
combinations = append(combinations, slice.Merge(group, otherGroup))
|
||||
combinations = append(combinations, collection.MergeSlices(group, otherGroup))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,9 +195,14 @@ func WithMatcherNCarryIndependentM[T Item, E generic.Ordered](n, m int, getType
|
|||
if len(group) != n {
|
||||
continue
|
||||
}
|
||||
ms := slice.LimitedCombinations(slice.SubWithCheck(items, group, func(a, b T) bool { return reflect.DeepEqual(a, b) }), m, m)
|
||||
|
||||
other := collection.CloneSlice(items)
|
||||
collection.DropSliceOverlappingElements(&other, group, func(source, target T) bool {
|
||||
return reflect.DeepEqual(source, target)
|
||||
})
|
||||
ms := collection.FindCombinationsInSliceByRange(other, m, m)
|
||||
for _, otherGroup := range ms {
|
||||
combinations = append(combinations, slice.Merge(group, otherGroup))
|
||||
combinations = append(combinations, collection.MergeSlices(group, otherGroup))
|
||||
}
|
||||
}
|
||||
return combinations
|
||||
|
|
|
@ -41,7 +41,7 @@ func ZIPUnCompress(dataByte []byte) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc.Close()
|
||||
_ = rc.Close()
|
||||
}
|
||||
return result.Bytes(), nil
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package concurrent
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -55,5 +55,5 @@ func (slf *Slice[T]) Clear() {
|
|||
func (slf *Slice[T]) GetData() []T {
|
||||
slf.rw.Lock()
|
||||
defer slf.rw.Unlock()
|
||||
return slice.Copy(slf.data)
|
||||
return collection.CloneSlice(slf.data)
|
||||
}
|
||||
|
|
|
@ -1,214 +0,0 @@
|
|||
package counter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewCounter 创建一个计数器
|
||||
func NewCounter[K comparable, V generic.Number]() *Counter[K, V] {
|
||||
c := &Counter[K, V]{
|
||||
c: make(map[K]any),
|
||||
dr: make(map[K]int64),
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// newSubCounter 创建一个子计数器
|
||||
func newSubCounter[K comparable, V generic.Number]() *Counter[K, V] {
|
||||
c := &Counter[K, V]{
|
||||
c: make(map[K]any),
|
||||
dr: make(map[K]int64),
|
||||
sub: true,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Counter 计数器
|
||||
type Counter[K comparable, V generic.Number] struct {
|
||||
lock sync.RWMutex
|
||||
sub bool
|
||||
dr map[K]int64
|
||||
drm map[string]int64
|
||||
c map[K]any
|
||||
}
|
||||
|
||||
// AddWithMark 添加计数,根据 mark 判断是否重复计数
|
||||
func (slf *Counter[K, V]) AddWithMark(mark, key K, value V, expired time.Duration) {
|
||||
slf.lock.Lock()
|
||||
if expired > 0 {
|
||||
now := time.Now().Unix()
|
||||
mk := fmt.Sprintf("%v_%v", mark, key)
|
||||
expiredTime, exist := slf.drm[mk]
|
||||
if exist {
|
||||
if expiredTime > now {
|
||||
slf.lock.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
slf.drm[mk] = now + int64(expired/time.Second)
|
||||
}
|
||||
|
||||
v, exist := slf.c[key]
|
||||
if !exist {
|
||||
slf.c[key] = value
|
||||
slf.lock.Unlock()
|
||||
return
|
||||
}
|
||||
if v, ok := v.(V); !ok {
|
||||
slf.lock.Unlock()
|
||||
panic("counter value is sub counter")
|
||||
} else {
|
||||
slf.c[key] = v + value
|
||||
}
|
||||
slf.lock.Unlock()
|
||||
}
|
||||
|
||||
// Add 添加计数
|
||||
// - 当设置了 expired 时,在 expired 时间内,不会重复计数
|
||||
// - 需要注意的是,当第一次设置了 expired,第二次未设置时,第二次的计数将生效
|
||||
func (slf *Counter[K, V]) Add(key K, value V, expired ...time.Duration) {
|
||||
slf.lock.Lock()
|
||||
if len(expired) > 0 && expired[0] > 0 {
|
||||
now := time.Now().Unix()
|
||||
expiredTime, exist := slf.dr[key]
|
||||
if exist {
|
||||
if expiredTime > now {
|
||||
slf.lock.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
slf.dr[key] = now + int64(expired[0]/time.Second)
|
||||
}
|
||||
|
||||
v, exist := slf.c[key]
|
||||
if !exist {
|
||||
slf.c[key] = value
|
||||
slf.lock.Unlock()
|
||||
return
|
||||
}
|
||||
if v, ok := v.(V); !ok {
|
||||
slf.lock.Unlock()
|
||||
panic("counter value is sub counter")
|
||||
} else {
|
||||
slf.c[key] = v + value
|
||||
}
|
||||
slf.lock.Unlock()
|
||||
}
|
||||
|
||||
// Get 获取计数
|
||||
func (slf *Counter[K, V]) Get(key K) V {
|
||||
slf.lock.RLock()
|
||||
v, exist := slf.c[key]
|
||||
if !exist {
|
||||
slf.lock.RUnlock()
|
||||
return 0
|
||||
}
|
||||
if v, ok := v.(V); !ok {
|
||||
slf.lock.RUnlock()
|
||||
panic("counter value is sub counter")
|
||||
} else {
|
||||
slf.lock.RUnlock()
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Reset 重置特定 key 的计数
|
||||
// - 当 key 为一个子计数器时,将会重置该子计数器
|
||||
func (slf *Counter[K, V]) Reset(key K) {
|
||||
slf.lock.Lock()
|
||||
delete(slf.c, key)
|
||||
delete(slf.dr, key)
|
||||
slf.lock.Unlock()
|
||||
}
|
||||
|
||||
// ResetMark 重置特定 mark 的 key 的计数
|
||||
func (slf *Counter[K, V]) ResetMark(mark, key K) {
|
||||
slf.lock.Lock()
|
||||
mk := fmt.Sprintf("%v_%v", mark, key)
|
||||
delete(slf.c, key)
|
||||
delete(slf.drm, mk)
|
||||
slf.lock.Unlock()
|
||||
}
|
||||
|
||||
// ResetExpired 重置特定 key 的过期时间
|
||||
func (slf *Counter[K, V]) ResetExpired(key K) {
|
||||
slf.lock.Lock()
|
||||
delete(slf.dr, key)
|
||||
slf.lock.Unlock()
|
||||
}
|
||||
|
||||
// ResetExpiredMark 重置特定 mark 的 key 的过期时间
|
||||
func (slf *Counter[K, V]) ResetExpiredMark(mark, key K) {
|
||||
slf.lock.Lock()
|
||||
mk := fmt.Sprintf("%v_%v", mark, key)
|
||||
delete(slf.drm, mk)
|
||||
slf.lock.Unlock()
|
||||
}
|
||||
|
||||
// ResetAll 重置所有计数
|
||||
func (slf *Counter[K, V]) ResetAll() {
|
||||
slf.lock.Lock()
|
||||
hash.Clear(slf.c)
|
||||
hash.Clear(slf.dr)
|
||||
hash.Clear(slf.drm)
|
||||
slf.lock.Unlock()
|
||||
}
|
||||
|
||||
// SubCounter 获取子计数器
|
||||
func (slf *Counter[K, V]) SubCounter(key K) *Counter[K, V] {
|
||||
slf.lock.Lock()
|
||||
v, exist := slf.c[key]
|
||||
if !exist {
|
||||
counter := newSubCounter[K, V]()
|
||||
slf.c[key] = counter
|
||||
slf.lock.Unlock()
|
||||
return counter
|
||||
}
|
||||
if v, ok := v.(*Counter[K, V]); !ok {
|
||||
slf.lock.Unlock()
|
||||
panic("counter value is count value")
|
||||
} else {
|
||||
slf.lock.Unlock()
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// GetCounts 获取计数器的所有计数
|
||||
func (slf *Counter[K, V]) GetCounts() map[K]V {
|
||||
counts := make(map[K]V)
|
||||
slf.lock.RLock()
|
||||
for k, v := range slf.c {
|
||||
if v, ok := v.(V); !ok {
|
||||
continue
|
||||
} else {
|
||||
counts[k] = v
|
||||
}
|
||||
}
|
||||
slf.lock.RUnlock()
|
||||
return counts
|
||||
}
|
||||
|
||||
// GetSubCounters 获取计数器的所有子计数器
|
||||
func (slf *Counter[K, V]) GetSubCounters() map[K]*Counter[K, V] {
|
||||
counters := make(map[K]*Counter[K, V])
|
||||
slf.lock.RLock()
|
||||
for k, v := range slf.c {
|
||||
if v, ok := v.(*Counter[K, V]); !ok {
|
||||
continue
|
||||
} else {
|
||||
counters[k] = v
|
||||
}
|
||||
}
|
||||
slf.lock.RUnlock()
|
||||
return counters
|
||||
}
|
||||
|
||||
// Shadow 获取该计数器的影子计数器,影子计数器的任何操作都不会影响到原计数器
|
||||
// - 适用于持久化等场景
|
||||
func (slf *Counter[K, V]) Shadow() *Shadow[K, V] {
|
||||
return newShadow(slf)
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package counter_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/counter"
|
||||
"github.com/kercylan98/minotaur/utils/times"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCounter_Add(t *testing.T) {
|
||||
c := counter.NewCounter[string, int64]()
|
||||
|
||||
c.Add("login_count", 1, times.GetNextDayInterval(time.Now()))
|
||||
c.Add("login_count", 1, times.GetNextDayInterval(time.Now()))
|
||||
c.SubCounter("live").Add("login_count", 1, times.GetNextDayInterval(time.Now()))
|
||||
|
||||
fmt.Println(c)
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package counter
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
)
|
||||
|
||||
func newShadow[K comparable, V generic.Number](counter *Counter[K, V]) *Shadow[K, V] {
|
||||
counter.lock.RLock()
|
||||
shadow := &Shadow[K, V]{
|
||||
Sub: counter.sub,
|
||||
DeduplicationRecord: hash.Copy(counter.dr),
|
||||
DeduplicationRecordMark: hash.Copy(counter.drm),
|
||||
}
|
||||
counter.lock.RUnlock()
|
||||
shadow.Counter = counter.GetCounts()
|
||||
subs := counter.GetSubCounters()
|
||||
for k, c := range subs {
|
||||
shadow.SubCounters[k] = newShadow(c)
|
||||
}
|
||||
return shadow
|
||||
}
|
||||
|
||||
// Shadow 计数器的影子计数器
|
||||
type Shadow[K comparable, V generic.Number] struct {
|
||||
Sub bool // 是否为子计数器
|
||||
DeduplicationRecord map[K]int64 // 最后一次写入时间
|
||||
DeduplicationRecordMark map[string]int64 // 最后一次写入时间
|
||||
Counter map[K]V // 计数器
|
||||
SubCounters map[K]*Shadow[K, V] // 子计数器
|
||||
}
|
||||
|
||||
// ToCounter 将影子计数器转换为计数器
|
||||
func (slf *Shadow[K, V]) ToCounter() *Counter[K, V] {
|
||||
return slf.toCounter(nil)
|
||||
}
|
||||
|
||||
// toCounter 将影子计数器转换为计数器
|
||||
func (slf *Shadow[K, V]) toCounter(parent *Counter[K, V]) *Counter[K, V] {
|
||||
counter := &Counter[K, V]{
|
||||
sub: slf.Sub,
|
||||
c: map[K]any{},
|
||||
}
|
||||
if slf.Sub {
|
||||
counter.dr = parent.dr
|
||||
} else {
|
||||
counter.dr = hash.Copy(slf.DeduplicationRecord)
|
||||
counter.drm = hash.Copy(slf.DeduplicationRecordMark)
|
||||
}
|
||||
for k, v := range slf.Counter {
|
||||
counter.c[k] = v
|
||||
}
|
||||
for k, s := range slf.SubCounters {
|
||||
counter.c[k] = s.toCounter(counter)
|
||||
}
|
||||
return counter
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package counter
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// NewSimpleDeduplication 创建一个简单去重计数器
|
||||
// - 该计数器不会记录每个 key 的计数,只会记录 key 的存在与否
|
||||
// - 当 key 不存在时,计数器会将 key 记录为存在,并将计数器增加特定的值
|
||||
func NewSimpleDeduplication[K comparable, V generic.Number]() *SimpleDeduplication[K, V] {
|
||||
return &SimpleDeduplication[K, V]{
|
||||
r: make(map[K]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// SimpleDeduplication 简单去重计数器
|
||||
type SimpleDeduplication[K comparable, V generic.Number] struct {
|
||||
c V
|
||||
r map[K]struct{}
|
||||
l sync.RWMutex
|
||||
}
|
||||
|
||||
// Add 添加计数,根据 key 判断是否重复计数
|
||||
func (slf *SimpleDeduplication[K, V]) Add(key K, value V) {
|
||||
slf.l.Lock()
|
||||
defer slf.l.Unlock()
|
||||
if _, exist := slf.r[key]; !exist {
|
||||
slf.r[key] = struct{}{}
|
||||
slf.c += value
|
||||
}
|
||||
}
|
||||
|
||||
// Get 获取计数
|
||||
func (slf *SimpleDeduplication[K, V]) Get() V {
|
||||
slf.l.RLock()
|
||||
defer slf.l.RUnlock()
|
||||
return slf.c
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
package deck
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
)
|
||||
|
||||
// NewDeck 创建一个新的甲板
|
||||
|
@ -21,7 +20,7 @@ type Deck[I Item] struct {
|
|||
|
||||
// AddGroup 将一个组添加到甲板中
|
||||
func (slf *Deck[I]) AddGroup(group *Group[I]) {
|
||||
if !hash.Exist(slf.groups, group.GetGuid()) {
|
||||
if !collection.FindInMapKey(slf.groups, group.GetGuid()) {
|
||||
slf.groups[group.GetGuid()] = group
|
||||
slf.sort = append(slf.sort, group.GetGuid())
|
||||
}
|
||||
|
@ -30,7 +29,7 @@ func (slf *Deck[I]) AddGroup(group *Group[I]) {
|
|||
// RemoveGroup 移除甲板中的一个组
|
||||
func (slf *Deck[I]) RemoveGroup(guid int64) {
|
||||
delete(slf.groups, guid)
|
||||
index := slice.GetIndex(slf.sort, guid)
|
||||
index := collection.FindIndexInComparableSlice(slf.sort, guid)
|
||||
if index != -1 {
|
||||
slf.sort = append(slf.sort[:index], slf.sort[index+1:]...)
|
||||
}
|
||||
|
@ -43,7 +42,7 @@ func (slf *Deck[I]) GetCount() int {
|
|||
|
||||
// GetGroups 获取所有组
|
||||
func (slf *Deck[I]) GetGroups() map[int64]*Group[I] {
|
||||
return hash.Copy(slf.groups)
|
||||
return collection.CloneMap(slf.groups)
|
||||
}
|
||||
|
||||
// GetGroupsSlice 获取所有组
|
||||
|
@ -57,7 +56,7 @@ func (slf *Deck[I]) GetGroupsSlice() []*Group[I] {
|
|||
|
||||
// GetNext 获取特定组的下一个组
|
||||
func (slf *Deck[I]) GetNext(guid int64) *Group[I] {
|
||||
index := slice.GetIndex(slf.sort, guid)
|
||||
index := collection.FindIndexInComparableSlice(slf.sort, guid)
|
||||
if index == -1 {
|
||||
return nil
|
||||
}
|
||||
|
@ -69,7 +68,7 @@ func (slf *Deck[I]) GetNext(guid int64) *Group[I] {
|
|||
|
||||
// GetPrev 获取特定组的上一个组
|
||||
func (slf *Deck[I]) GetPrev(guid int64) *Group[I] {
|
||||
index := slice.GetIndex(slf.sort, guid)
|
||||
index := collection.FindIndexInComparableSlice(slf.sort, guid)
|
||||
if index == -1 {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package file
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -150,12 +150,12 @@ func Paths(dir string) []string {
|
|||
return paths
|
||||
}
|
||||
|
||||
// ReadLineWithParallelByChannel 并行的分行读取文件并行处理,处理过程中会将每一行的内容传入 handlerFunc 中进行处理
|
||||
// ReadLineWithParallel 并行的分行读取文件并行处理,处理过程中会将每一行的内容传入 handlerFunc 中进行处理
|
||||
// - 由于是并行处理,所以处理过程中的顺序是不确定的。
|
||||
// - 可通过 start 参数指定开始读取的位置,如果不指定则从文件开头开始读取。
|
||||
func ReadLineWithParallel(filename string, chunkSize int64, handlerFunc func(string), start ...int64) (n int64, err error) {
|
||||
file, err := os.Open(filename)
|
||||
offset := slice.GetValue(start, 0)
|
||||
offset := collection.FindFirstOrDefaultInSlice(start, 0)
|
||||
if err != nil {
|
||||
return offset, err
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package fsm
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
)
|
||||
|
||||
// NewFSM 创建一个新的状态机
|
||||
|
@ -60,7 +60,7 @@ func (slf *FSM[State, Data]) Unregister(state State) {
|
|||
|
||||
// HasState 检查状态机是否存在特定状态
|
||||
func (slf *FSM[State, Data]) HasState(state State) bool {
|
||||
return hash.Exist(slf.states, state)
|
||||
return collection.FindInMapKey(slf.states, state)
|
||||
}
|
||||
|
||||
// Change 改变状态机状态到新的状态
|
||||
|
|
|
@ -3,8 +3,8 @@ package geometry
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -317,7 +317,7 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
|
|||
for i, directions := range [][]Direction{DirectionUDLR, DirectionLRUD} {
|
||||
var direction Direction
|
||||
for {
|
||||
next, direction = slice.NextLoop(directions, next)
|
||||
next, direction = collection.FindLoopedNextInSlice(directions, next)
|
||||
for {
|
||||
directionPoint = GetDirectionNextWithPoint(direction, directionPoint)
|
||||
if px, py := directionPoint.GetXY(); px < 0 || px >= areaWidth || py < 0 || py >= areaHeight {
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package hash
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewConsistency(replicas int) *Consistency {
|
||||
return &Consistency{
|
||||
replicas: replicas,
|
||||
}
|
||||
}
|
||||
|
||||
// Consistency 一致性哈希生成
|
||||
//
|
||||
// https://blog.csdn.net/zhpCSDN921011/article/details/126845397
|
||||
type Consistency struct {
|
||||
replicas int // 虚拟节点的数量
|
||||
keys []int // 所有虚拟节点的哈希值
|
||||
hashMap map[int]int // 虚拟节点的哈希值: 节点(虚拟节点映射到真实节点)
|
||||
}
|
||||
|
||||
// AddNode 添加节点
|
||||
func (slf *Consistency) AddNode(keys ...int) {
|
||||
if slf.hashMap == nil {
|
||||
slf.hashMap = map[int]int{}
|
||||
}
|
||||
if slf.replicas == 0 {
|
||||
slf.replicas = 3
|
||||
}
|
||||
for _, key := range keys {
|
||||
for i := 0; i < slf.replicas; i++ {
|
||||
// 计算虚拟节点哈希值
|
||||
hash := int(crc32.ChecksumIEEE([]byte(strconv.Itoa(i) + strconv.Itoa(key))))
|
||||
|
||||
// 存储虚拟节点哈希值
|
||||
slf.keys = append(slf.keys, hash)
|
||||
|
||||
// 存入map做映射
|
||||
slf.hashMap[hash] = key
|
||||
}
|
||||
}
|
||||
|
||||
//排序哈希值,下面匹配的时候要二分搜索
|
||||
sort.Ints(slf.keys)
|
||||
}
|
||||
|
||||
// PickNode 获取与 key 最接近的节点
|
||||
func (slf *Consistency) PickNode(key any) int {
|
||||
if len(slf.keys) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
partitionKey := fmt.Sprintf("%#v", key)
|
||||
beg := strings.Index(partitionKey, "{")
|
||||
end := strings.Index(partitionKey, "}")
|
||||
if beg != -1 && !(end == -1 || end == beg+1) {
|
||||
partitionKey = partitionKey[beg+1 : end]
|
||||
}
|
||||
|
||||
// 计算传入key的哈希值
|
||||
hash := int(crc32.ChecksumIEEE([]byte(partitionKey)))
|
||||
|
||||
// sort.Search使用二分查找满足m.Keys[i]>=hash的最小哈希值
|
||||
idx := sort.Search(len(slf.keys), func(i int) bool { return slf.keys[i] >= hash })
|
||||
|
||||
// 若key的hash值大于最后一个虚拟节点的hash值,则选择第一个虚拟节点
|
||||
if idx == len(slf.keys) {
|
||||
idx = 0
|
||||
}
|
||||
|
||||
return slf.hashMap[slf.keys[idx]]
|
||||
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package hash
|
||||
|
||||
// ToSlice 将 map 的 value 转换为切片
|
||||
func ToSlice[K comparable, V any](m map[K]V) []V {
|
||||
var s = make([]V, 0, len(m))
|
||||
for _, v := range m {
|
||||
s = append(s, v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// KeyToSlice 将 map 的 key 转换为切片
|
||||
func KeyToSlice[K comparable, V any](m map[K]V) []K {
|
||||
var s = make([]K, 0, len(m))
|
||||
for k := range m {
|
||||
s = append(s, k)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Reversal 将 map 的 key 和 value 互换
|
||||
func Reversal[K comparable, V comparable](m map[K]V) map[V]K {
|
||||
var nm = make(map[V]K)
|
||||
for k, v := range m {
|
||||
nm[v] = k
|
||||
}
|
||||
return nm
|
||||
}
|
||||
|
||||
// ToMap 将切片转换为 map
|
||||
func ToMap[V any](slice []V) map[int]V {
|
||||
var m = make(map[int]V)
|
||||
for i, v := range slice {
|
||||
m[i] = v
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// ToIterator 将切片转换为 Iterator
|
||||
func ToIterator[V comparable](slice []V) map[V]struct{} {
|
||||
var m = make(map[V]struct{})
|
||||
for _, v := range slice {
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// ToMapBool 将切片转换为 map,value作为Key
|
||||
func ToMapBool[V comparable](slice []V) map[V]bool {
|
||||
var m = make(map[V]bool)
|
||||
for _, v := range slice {
|
||||
m[v] = true
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// ToSortMap 将切片转换为 SortMap
|
||||
func ToSortMap[V any](slice []V) SortMap[int, V] {
|
||||
var m SortMap[int, V]
|
||||
for i, v := range slice {
|
||||
m.Set(i, v)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Copy 复制一个map
|
||||
func Copy[K comparable, V any](m map[K]V) map[K]V {
|
||||
var backup = make(map[K]V)
|
||||
for k, v := range m {
|
||||
backup[k] = v
|
||||
}
|
||||
return backup
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package hash
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// Exist 检查特定 key 是否存在
|
||||
func Exist[K comparable, V any](m map[K]V, key K) bool {
|
||||
_, exist := m[key]
|
||||
return exist
|
||||
}
|
||||
|
||||
// AllExist 检查多个 key 是否存在
|
||||
func AllExist[K comparable, V any](m map[K]V, keys ...K) bool {
|
||||
for key := range m {
|
||||
if _, exist := m[key]; !exist {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ToJson 将 map 转换为 json 字符串
|
||||
func ToJson[K comparable, V any](m map[K]V) string {
|
||||
if data, err := json.Marshal(m); err == nil {
|
||||
return string(data)
|
||||
}
|
||||
return "{}"
|
||||
}
|
||||
|
||||
// RandomGet 随机获取一个元素
|
||||
func RandomGet[K comparable, V any](m map[K]V) (v V) {
|
||||
for _, v := range m {
|
||||
return v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RandomGetKey 随机获取一个 key
|
||||
func RandomGetKey[K comparable, V any](m map[K]V) (k K) {
|
||||
for k = range m {
|
||||
return k
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RandomGetN 随机获取 n 个元素
|
||||
// - 获取到的元素不会是重复的,当 map 的长度不足 n 时,返回的元素等同于 hash.ToSlice
|
||||
func RandomGetN[K comparable, V any](m map[K]V, n int) (vs []V) {
|
||||
for _, v := range m {
|
||||
vs = append(vs, v)
|
||||
if len(vs) >= n {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RandomGetKeyN 随机获取 n 个 key
|
||||
// - 获取到的元素不会是重复的,当 map 的长度不足 n 时,返回的元素等同于 hash.KeyToSlice
|
||||
func RandomGetKeyN[K comparable, V any](m map[K]V, n int) (ks []K) {
|
||||
for k := range m {
|
||||
ks = append(ks, k)
|
||||
if len(ks) >= n {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Clear 清空 map
|
||||
func Clear[K comparable, V any](m map[K]V) {
|
||||
for k := range m {
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package hash
|
||||
|
||||
import "github.com/kercylan98/minotaur/utils/generic"
|
||||
|
||||
// Sum 计算一个 map 中的 value 总和
|
||||
func Sum[K comparable, V generic.Number](m map[K]V) V {
|
||||
var sum V
|
||||
for _, v := range m {
|
||||
sum += v
|
||||
}
|
||||
return sum
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
package hash
|
||||
|
||||
// NewSet 创建一个 Set 集合
|
||||
func NewSet[K comparable](ks ...K) Set[K] {
|
||||
s := make(Set[K])
|
||||
s.AddAll(ks...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Set 集合
|
||||
type Set[K comparable] map[K]struct{}
|
||||
|
||||
// Exist 检查特定 key 是否存在
|
||||
func (s Set[K]) Exist(key K) bool {
|
||||
_, exist := s[key]
|
||||
return exist
|
||||
}
|
||||
|
||||
// AllExist 检查多个 key 是否存在
|
||||
func (s Set[K]) AllExist(keys ...K) bool {
|
||||
for _, key := range keys {
|
||||
if _, exist := s[key]; !exist {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Add 添加元素
|
||||
func (s Set[K]) Add(key K) {
|
||||
s[key] = struct{}{}
|
||||
}
|
||||
|
||||
// AddAll 添加多个元素
|
||||
func (s Set[K]) AddAll(keys ...K) {
|
||||
for _, key := range keys {
|
||||
s[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove 移除元素
|
||||
func (s Set[K]) Remove(key K) {
|
||||
delete(s, key)
|
||||
}
|
||||
|
||||
// RemoveAll 移除多个元素
|
||||
func (s Set[K]) RemoveAll(keys ...K) {
|
||||
for _, key := range keys {
|
||||
delete(s, key)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear 清空集合
|
||||
func (s Set[K]) Clear() {
|
||||
for key := range s {
|
||||
delete(s, key)
|
||||
}
|
||||
}
|
||||
|
||||
// Size 集合长度
|
||||
func (s Set[K]) Size() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// ToSlice 转换为切片
|
||||
func (s Set[K]) ToSlice() []K {
|
||||
keys := make([]K, 0, len(s))
|
||||
for key := range s {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// ToMap 转换为 map
|
||||
func (s Set[K]) ToMap() map[K]struct{} {
|
||||
return s
|
||||
}
|
||||
|
||||
// ToJson 转换为 json 字符串
|
||||
func (s Set[K]) ToJson() string {
|
||||
return ToJson(s)
|
||||
}
|
||||
|
||||
// RandomGet 随机获取一个元素
|
||||
func (s Set[K]) RandomGet() (k K) {
|
||||
for k = range s {
|
||||
return k
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package hash
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
func NewSortMap[K comparable, V any]() *SortMap[K, V] {
|
||||
return &SortMap[K, V]{
|
||||
m: map[K]V{},
|
||||
s: map[int]K{},
|
||||
r: map[K]int{},
|
||||
}
|
||||
}
|
||||
|
||||
// SortMap 有序的 map 实现
|
||||
type SortMap[K comparable, V any] struct {
|
||||
i int
|
||||
m map[K]V
|
||||
s map[int]K
|
||||
r map[K]int
|
||||
}
|
||||
|
||||
func (slf *SortMap[K, V]) Set(key K, value V) {
|
||||
if i, exist := slf.r[key]; exist {
|
||||
slf.s[i] = key
|
||||
slf.m[key] = value
|
||||
} else {
|
||||
slf.m[key] = value
|
||||
slf.s[slf.i] = key
|
||||
slf.r[key] = slf.i
|
||||
slf.i++
|
||||
}
|
||||
}
|
||||
|
||||
func (slf *SortMap[K, V]) Del(key K) {
|
||||
if _, exist := slf.m[key]; exist {
|
||||
delete(slf.s, slf.r[key])
|
||||
delete(slf.r, key)
|
||||
delete(slf.m, key)
|
||||
}
|
||||
}
|
||||
|
||||
func (slf *SortMap[K, V]) Get(key K) V {
|
||||
v := slf.m[key]
|
||||
return v
|
||||
}
|
||||
|
||||
func (slf *SortMap[K, V]) For(handle func(key K, value V) bool) {
|
||||
for k, v := range slf.m {
|
||||
if !handle(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (slf *SortMap[K, V]) ForSort(handle func(key K, value V) bool) {
|
||||
var indexes []int
|
||||
for i := range slf.s {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
sort.Ints(indexes)
|
||||
for _, i := range indexes {
|
||||
k := slf.s[i]
|
||||
if !handle(k, slf.m[k]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (slf *SortMap[K, V]) ToMap() map[K]V {
|
||||
var m = make(map[K]V)
|
||||
for k, v := range slf.m {
|
||||
m[k] = v
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (slf *SortMap[K, V]) ToSlice() []V {
|
||||
var s = make([]V, 0, len(slf.m))
|
||||
for _, v := range slf.m {
|
||||
s = append(s, v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (slf *SortMap[K, V]) ToSliceSort() []V {
|
||||
var indexes []int
|
||||
for i := range slf.s {
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
sort.Ints(indexes)
|
||||
var result []V
|
||||
for _, i := range indexes {
|
||||
k := slf.s[i]
|
||||
result = append(result, slf.m[k])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (slf *SortMap[K, V]) KeyToSlice() []K {
|
||||
var s = make([]K, 0, len(slf.m))
|
||||
for k := range slf.m {
|
||||
s = append(s, k)
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package maths
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"sort"
|
||||
)
|
||||
|
||||
|
@ -35,7 +35,7 @@ func Compare[V generic.Ordered](a V, expression CompareExpression, b V) bool {
|
|||
}
|
||||
|
||||
// IsContinuity 检查一组值是否连续
|
||||
func IsContinuity[V generic.Integer](values []V) bool {
|
||||
func IsContinuity[S ~[]V, V generic.Integer](values S) bool {
|
||||
length := len(values)
|
||||
if length == 0 {
|
||||
return false
|
||||
|
@ -52,10 +52,10 @@ func IsContinuity[V generic.Integer](values []V) bool {
|
|||
}
|
||||
|
||||
// IsContinuityWithSort 检查一组值排序后是否连续
|
||||
func IsContinuityWithSort[V generic.Integer](values []V) bool {
|
||||
sli := slice.Copy(values)
|
||||
sort.Slice(sli, func(i, j int) bool {
|
||||
return sli[i] < sli[j]
|
||||
func IsContinuityWithSort[S ~[]V, V generic.Integer](values S) bool {
|
||||
s := collection.CloneSlice(values)
|
||||
sort.Slice(s, func(i, j int) bool {
|
||||
return s[i] < s[j]
|
||||
})
|
||||
return IsContinuity(sli)
|
||||
return IsContinuity(s)
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ func UnMergeInt64[V generic.SignedNumber](n int64) (V, V) {
|
|||
|
||||
// ToContinuous 将一组非连续的数字转换为从1开始的连续数字
|
||||
// - 返回值是一个 map,key 是从 1 开始的连续数字,value 是原始数字
|
||||
func ToContinuous[V generic.Integer](nums []V) map[V]V {
|
||||
func ToContinuous[S ~[]V, V generic.Integer](nums S) map[V]V {
|
||||
if len(nums) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package memory
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/collection"
|
||||
"github.com/kercylan98/minotaur/utils/random"
|
||||
"github.com/kercylan98/minotaur/utils/super"
|
||||
"reflect"
|
||||
|
@ -81,7 +81,7 @@ func BindPersistCacheProgram[OutputParamHandlerFunc any](name string, handler Ou
|
|||
cachesRWMutex.RUnlock()
|
||||
return
|
||||
}
|
||||
funcCache = hash.Copy(funcCache)
|
||||
funcCache = collection.CloneMap(funcCache)
|
||||
cachesRWMutex.RUnlock()
|
||||
for _, results := range funcCache {
|
||||
persist.Call(results)
|
||||
|
@ -111,7 +111,7 @@ func BindPersistCacheProgram[OutputParamHandlerFunc any](name string, handler Ou
|
|||
cachesRWMutex.RUnlock()
|
||||
return
|
||||
}
|
||||
funcCache = hash.Copy(funcCache)
|
||||
funcCache = collection.CloneMap(funcCache)
|
||||
cachesRWMutex.RUnlock()
|
||||
delay := opt.delay
|
||||
tick := delay
|
||||
|
|
|
@ -1,308 +0,0 @@
|
|||
package sher
|
||||
|
||||
import "sort"
|
||||
|
||||
type ComparisonHandler[V any] func(source, target V) bool
|
||||
|
||||
// InSlice 判断切片中是否包含某个元素
|
||||
func InSlice[S ~[]V, V any](slice S, v V, handler ComparisonHandler[V]) bool {
|
||||
if slice == nil {
|
||||
return false
|
||||
}
|
||||
for _, value := range slice {
|
||||
if handler(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InSliceByBinarySearch 判断切片中是否包含某个元素,使用二分搜索
|
||||
func InSliceByBinarySearch[S ~[]V, V any](slice S, v V, handler ComparisonHandler[V]) bool {
|
||||
return sort.Search(len(slice), func(i int) bool {
|
||||
return handler(v, slice[i])
|
||||
}) != len(slice)
|
||||
}
|
||||
|
||||
// AllInSlice 判断切片中是否包含所有元素
|
||||
func AllInSlice[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
||||
if slice == nil {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if !InSlice(slice, value, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AllInSliceByBinarySearch 判断切片中是否包含所有元素,使用二分搜索
|
||||
func AllInSliceByBinarySearch[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
||||
if slice == nil {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if !InSliceByBinarySearch(slice, value, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyInSlice 判断切片中是否包含任意一个元素
|
||||
func AnyInSlice[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
||||
if slice == nil {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if InSlice(slice, value, handler) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyInSliceByBinarySearch 判断切片中是否包含任意一个元素,使用二分搜索
|
||||
func AnyInSliceByBinarySearch[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
||||
if slice == nil {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if InSliceByBinarySearch(slice, value, handler) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InSlices 判断多个切片中是否包含某个元素
|
||||
func InSlices[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
||||
return InSlice(MergeSlices(slices...), v, handler)
|
||||
}
|
||||
|
||||
// InSlicesByBinarySearch 判断多个切片中是否包含某个元素,使用二分搜索
|
||||
func InSlicesByBinarySearch[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
||||
return InSliceByBinarySearch(MergeSlices(slices...), v, handler)
|
||||
}
|
||||
|
||||
// AllInSlices 判断多个切片中是否包含所有元素
|
||||
func AllInSlices[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
||||
return AllInSlice(MergeSlices(slices...), values, handler)
|
||||
}
|
||||
|
||||
// AllInSlicesByBinarySearch 判断多个切片中是否包含所有元素,使用二分搜索
|
||||
func AllInSlicesByBinarySearch[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
||||
return AllInSliceByBinarySearch(MergeSlices(slices...), values, handler)
|
||||
}
|
||||
|
||||
// AnyInSlices 判断多个切片中是否包含任意一个元素
|
||||
func AnyInSlices[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
||||
return AnyInSlice(MergeSlices(slices...), values, handler)
|
||||
}
|
||||
|
||||
// AnyInSlicesByBinarySearch 判断多个切片中是否包含任意一个元素,使用二分搜索
|
||||
func AnyInSlicesByBinarySearch[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
||||
return AnyInSliceByBinarySearch(MergeSlices(slices...), values, handler)
|
||||
}
|
||||
|
||||
// InAllSlices 判断元素是否在所有切片中都存在
|
||||
func InAllSlices[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
||||
if slices == nil {
|
||||
return false
|
||||
}
|
||||
for _, slice := range slices {
|
||||
if !InSlice(slice, v, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// InAllSlicesByBinarySearch 判断元素是否在所有切片中都存在,使用二分搜索
|
||||
func InAllSlicesByBinarySearch[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
||||
if slices == nil {
|
||||
return false
|
||||
}
|
||||
for _, slice := range slices {
|
||||
if !InSliceByBinarySearch(slice, v, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyInAllSlices 判断元素是否在所有切片中都存在任意至少一个
|
||||
func AnyInAllSlices[S ~[]V, V any](slices []S, v []V, handler ComparisonHandler[V]) bool {
|
||||
if slices == nil {
|
||||
return false
|
||||
}
|
||||
for _, slice := range slices {
|
||||
if AnyInSlice(slice, v, handler) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyInAllSlicesByBinarySearch 判断元素是否在所有切片中都存在任意至少一个,使用二分搜索
|
||||
func AnyInAllSlicesByBinarySearch[S ~[]V, V any](slices []S, v []V, handler ComparisonHandler[V]) bool {
|
||||
if slices == nil {
|
||||
return false
|
||||
}
|
||||
for _, slice := range slices {
|
||||
if AnyInSliceByBinarySearch(slice, v, handler) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// KeyInMap 判断 map 中是否包含某个 key
|
||||
func KeyInMap[M ~map[K]V, K comparable, V any](m M, key K) bool {
|
||||
_, ok := m[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ValueInMap 判断 map 中是否包含某个 value
|
||||
func ValueInMap[M ~map[K]V, K comparable, V any](m M, value V, handler ComparisonHandler[V]) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
for _, v := range m {
|
||||
if handler(value, v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AllKeyInMap 判断 map 中是否包含所有 key
|
||||
func AllKeyInMap[M ~map[K]V, K comparable, V any](m M, keys ...K) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
for _, key := range keys {
|
||||
if !KeyInMap(m, key) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AllValueInMap 判断 map 中是否包含所有 value
|
||||
func AllValueInMap[M ~map[K]V, K comparable, V any](m M, values []V, handler ComparisonHandler[V]) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if !ValueInMap(m, value, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyKeyInMap 判断 map 中是否包含任意一个 key
|
||||
func AnyKeyInMap[M ~map[K]V, K comparable, V any](m M, keys ...K) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
for _, key := range keys {
|
||||
if KeyInMap(m, key) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyValueInMap 判断 map 中是否包含任意一个 value
|
||||
func AnyValueInMap[M ~map[K]V, K comparable, V any](m M, values []V, handler ComparisonHandler[V]) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
if ValueInMap(m, value, handler) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AllKeyInMaps 判断多个 map 中是否包含所有 key
|
||||
func AllKeyInMaps[M ~map[K]V, K comparable, V any](maps []M, keys ...K) bool {
|
||||
if maps == nil {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if !AllKeyInMap(m, keys...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AllValueInMaps 判断多个 map 中是否包含所有 value
|
||||
func AllValueInMaps[M ~map[K]V, K comparable, V any](maps []M, values []V, handler ComparisonHandler[V]) bool {
|
||||
if maps == nil {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if !AllValueInMap(m, values, handler) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyKeyInMaps 判断多个 map 中是否包含任意一个 key
|
||||
func AnyKeyInMaps[M ~map[K]V, K comparable, V any](maps []M, keys ...K) bool {
|
||||
if maps == nil {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if AnyKeyInMap(m, keys...) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyValueInMaps 判断多个 map 中是否包含任意一个 value
|
||||
func AnyValueInMaps[M ~map[K]V, K comparable, V any](maps []M, values []V, handler ComparisonHandler[V]) bool {
|
||||
if maps == nil {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if AnyValueInMap(m, values, handler) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InAllMaps 判断元素是否在所有 map 中都存在
|
||||
func InAllMaps[M ~map[K]V, K comparable, V any](maps []M, key K) bool {
|
||||
if maps == nil {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if !KeyInMap(m, key) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AnyInAllMaps 判断元素是否在所有 map 中都存在任意至少一个
|
||||
func AnyInAllMaps[M ~map[K]V, K comparable, V any](maps []M, keys []K) bool {
|
||||
if maps == nil {
|
||||
return false
|
||||
}
|
||||
for _, m := range maps {
|
||||
if AnyKeyInMap(m, keys...) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
// Package sher 用于对 slice 和 map 操作的工具函数,将逐步替代 utils/slice 和 utils/map
|
||||
package sher
|
|
@ -1,118 +0,0 @@
|
|||
package sher_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/sher"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDeduplicateSliceInPlace(t *testing.T) {
|
||||
var cases = []struct {
|
||||
s []int
|
||||
expected []int
|
||||
}{
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
sher.DeduplicateSliceInPlace(&c.s)
|
||||
if len(c.s) != len(c.expected) {
|
||||
t.Errorf("DeduplicateSliceInPlace(%v) == %v, expected %v", c.s, c.s, c.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeduplicateSlice(t *testing.T) {
|
||||
var cases = []struct {
|
||||
s []int
|
||||
expected []int
|
||||
}{
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
sl := len(c.s)
|
||||
if r := sher.DeduplicateSlice(c.s); len(r) != len(c.expected) || len(c.s) != sl {
|
||||
t.Errorf("DeduplicateSlice(%v) == %v, expected %v", c.s, r, c.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeduplicateSliceInPlaceWithCompare(t *testing.T) {
|
||||
var cases = []struct {
|
||||
s []int
|
||||
expected []int
|
||||
}{
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
sher.DeduplicateSliceInPlaceWithCompare(&c.s, func(a, b int) bool {
|
||||
return a == b
|
||||
})
|
||||
if len(c.s) != len(c.expected) {
|
||||
t.Errorf("DeduplicateSliceInPlaceWithCompare(%v) == %v, expected %v", c.s, c.s, c.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeduplicateSliceWithCompare(t *testing.T) {
|
||||
var cases = []struct {
|
||||
s []int
|
||||
expected []int
|
||||
}{
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
{
|
||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2},
|
||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
sl := len(c.s)
|
||||
if r := sher.DeduplicateSliceWithCompare(c.s, func(a, b int) bool {
|
||||
return a == b
|
||||
}); len(r) != len(c.expected) || len(c.s) != sl {
|
||||
t.Errorf("DeduplicateSliceWithCompare(%v) == %v, expected %v", c.s, r, c.expected)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
package sher
|
||||
|
||||
import "github.com/kercylan98/minotaur/utils/generic"
|
||||
|
||||
// FindInSlice 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1
|
||||
func FindInSlice[S ~[]V, V any](slice S, handler func(v V) bool) (i int, t V) {
|
||||
if slice == nil {
|
||||
return -1, t
|
||||
}
|
||||
for i, v := range slice {
|
||||
if handler(v) {
|
||||
return i, v
|
||||
}
|
||||
}
|
||||
return -1, t
|
||||
}
|
||||
|
||||
// FindInSliceByBinary 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1
|
||||
func FindInSliceByBinary[S ~[]V, V any](slice S, handler func(v V) bool) (i int, t V) {
|
||||
low := 0
|
||||
high := len(slice) - 1
|
||||
|
||||
for low <= high {
|
||||
mid := low + (high-low)/2
|
||||
if handler(slice[mid]) {
|
||||
return mid, slice[mid]
|
||||
} else if handler(slice[mid]) {
|
||||
high = mid - 1
|
||||
} else {
|
||||
low = mid + 1
|
||||
}
|
||||
}
|
||||
return -1, t
|
||||
}
|
||||
|
||||
// FindMinimumInSlice 获取切片中的最小值
|
||||
func FindMinimumInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (result V) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
result = slice[0]
|
||||
for i := 1; i < len(slice); i++ {
|
||||
if handler(slice[i], result) {
|
||||
result = slice[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindMaximumInSlice 获取切片中的最大值
|
||||
func FindMaximumInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (result V) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
result = slice[0]
|
||||
for i := 1; i < len(slice); i++ {
|
||||
if handler(result, slice[i]) {
|
||||
result = slice[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindMin2MaxInSlice 获取切片中的最小值和最大值
|
||||
func FindMin2MaxInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (min, max V) {
|
||||
if slice == nil {
|
||||
return
|
||||
}
|
||||
min = slice[0]
|
||||
max = slice[0]
|
||||
for i := 1; i < len(slice); i++ {
|
||||
if handler(slice[i], min) {
|
||||
min = slice[i]
|
||||
}
|
||||
if handler(max, slice[i]) {
|
||||
max = slice[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindMinFromMap 获取 map 中的最小值
|
||||
func FindMinFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (result V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
for _, v := range m {
|
||||
if handler(v, result) {
|
||||
result = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindMaxFromMap 获取 map 中的最大值
|
||||
func FindMaxFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (result V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
for _, v := range m {
|
||||
if handler(result, v) {
|
||||
result = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindMin2MaxFromMap 获取 map 中的最小值和最大值
|
||||
func FindMin2MaxFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (min, max V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
for _, v := range m {
|
||||
if handler(v, min) {
|
||||
min = v
|
||||
}
|
||||
if handler(max, v) {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package slice_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChunk(t *testing.T) {
|
||||
var collection = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
var chunks = slice.Chunk(collection, 3)
|
||||
for _, chunk := range chunks {
|
||||
t.Log(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleChunk() {
|
||||
var collection = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
var chunks = slice.Chunk(collection, 3)
|
||||
for _, chunk := range chunks {
|
||||
fmt.Println(chunk)
|
||||
}
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
// [4 5 6]
|
||||
// [7 8 9]
|
||||
}
|
||||
|
||||
func BenchmarkChunk(b *testing.B) {
|
||||
var collection = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
slice.Chunk(collection, 3)
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package slice_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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))
|
||||
}
|
||||
|
||||
func ExampleDrop() {
|
||||
fmt.Println(slice.Drop(1, 3, []int{1, 2, 3, 4, 5}))
|
||||
// Output:
|
||||
// [1 5]
|
||||
}
|
||||
|
||||
func BenchmarkDrop(b *testing.B) {
|
||||
s := []int{1, 2, 3, 4, 5}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
slice.Drop(1, 3, s)
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
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)
|
||||
}
|
||||
|
||||
// FilterTCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 true
|
||||
func FilterTCopy[V any](slice []V, expression func(index int, item V) bool) []V {
|
||||
return FilterCopy(true, slice, expression)
|
||||
}
|
||||
|
||||
// FilterFCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 false
|
||||
func FilterFCopy[V any](slice []V, expression func(index int, item V) bool) []V {
|
||||
return FilterCopy(false, slice, expression)
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
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)
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
package slice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// NewPriority 创建一个优先级切片
|
||||
func NewPriority[V any](lengthAndCap ...int) *Priority[V] {
|
||||
p := &Priority[V]{}
|
||||
if len(lengthAndCap) > 0 {
|
||||
var length = lengthAndCap[0]
|
||||
var c int
|
||||
if len(lengthAndCap) > 1 {
|
||||
c = lengthAndCap[1]
|
||||
}
|
||||
p.items = make([]*PriorityItem[V], length, c)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Priority 是一个优先级切片
|
||||
type Priority[V any] struct {
|
||||
items []*PriorityItem[V]
|
||||
}
|
||||
|
||||
// Len 返回切片长度
|
||||
func (slf *Priority[V]) Len() int {
|
||||
return len(slf.items)
|
||||
}
|
||||
|
||||
// Cap 返回切片容量
|
||||
func (slf *Priority[V]) Cap() int {
|
||||
return cap(slf.items)
|
||||
}
|
||||
|
||||
// Clear 清空切片
|
||||
func (slf *Priority[V]) Clear() {
|
||||
slf.items = slf.items[:0]
|
||||
}
|
||||
|
||||
// Append 添加元素
|
||||
func (slf *Priority[V]) Append(v V, priority int) {
|
||||
slf.items = append(slf.items, NewPriorityItem[V](v, priority))
|
||||
slf.sort()
|
||||
}
|
||||
|
||||
// Appends 添加元素
|
||||
func (slf *Priority[V]) Appends(priority int, vs ...V) {
|
||||
for _, v := range vs {
|
||||
slf.Append(v, priority)
|
||||
}
|
||||
slf.sort()
|
||||
}
|
||||
|
||||
// Get 获取元素
|
||||
func (slf *Priority[V]) Get(index int) *PriorityItem[V] {
|
||||
return slf.items[index]
|
||||
}
|
||||
|
||||
// GetValue 获取元素值
|
||||
func (slf *Priority[V]) GetValue(index int) V {
|
||||
return slf.items[index].Value()
|
||||
}
|
||||
|
||||
// GetPriority 获取元素优先级
|
||||
func (slf *Priority[V]) GetPriority(index int) int {
|
||||
return slf.items[index].Priority()
|
||||
}
|
||||
|
||||
// Set 设置元素
|
||||
func (slf *Priority[V]) Set(index int, value V, priority int) {
|
||||
before := slf.items[index]
|
||||
slf.items[index] = NewPriorityItem[V](value, priority)
|
||||
if before.Priority() != priority {
|
||||
slf.sort()
|
||||
}
|
||||
}
|
||||
|
||||
// SetValue 设置元素值
|
||||
func (slf *Priority[V]) SetValue(index int, value V) {
|
||||
slf.items[index].v = value
|
||||
}
|
||||
|
||||
// SetPriority 设置元素优先级
|
||||
func (slf *Priority[V]) SetPriority(index int, priority int) {
|
||||
slf.items[index].p = priority
|
||||
slf.sort()
|
||||
}
|
||||
|
||||
// Action 直接操作切片,如果返回值不为 nil,则替换切片
|
||||
func (slf *Priority[V]) Action(action func(items []*PriorityItem[V]) []*PriorityItem[V]) {
|
||||
if len(slf.items) == 0 {
|
||||
return
|
||||
}
|
||||
if replace := action(slf.items); replace != nil {
|
||||
slf.items = replace
|
||||
slf.sort()
|
||||
}
|
||||
}
|
||||
|
||||
// Range 遍历切片,如果返回值为 false,则停止遍历
|
||||
func (slf *Priority[V]) Range(action func(index int, item *PriorityItem[V]) bool) {
|
||||
for i, item := range slf.items {
|
||||
if !action(i, item) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RangeValue 遍历切片值,如果返回值为 false,则停止遍历
|
||||
func (slf *Priority[V]) RangeValue(action func(index int, value V) bool) {
|
||||
slf.Range(func(index int, item *PriorityItem[V]) bool {
|
||||
return action(index, item.Value())
|
||||
})
|
||||
}
|
||||
|
||||
// RangePriority 遍历切片优先级,如果返回值为 false,则停止遍历
|
||||
func (slf *Priority[V]) RangePriority(action func(index int, priority int) bool) {
|
||||
slf.Range(func(index int, item *PriorityItem[V]) bool {
|
||||
return action(index, item.Priority())
|
||||
})
|
||||
}
|
||||
|
||||
// Slice 返回切片
|
||||
func (slf *Priority[V]) Slice() []V {
|
||||
var vs []V
|
||||
for _, item := range slf.items {
|
||||
vs = append(vs, item.Value())
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// String 返回切片字符串
|
||||
func (slf *Priority[V]) String() string {
|
||||
var vs []V
|
||||
for _, item := range slf.items {
|
||||
vs = append(vs, item.Value())
|
||||
}
|
||||
return fmt.Sprint(vs)
|
||||
}
|
||||
|
||||
// sort 排序
|
||||
func (slf *Priority[V]) sort() {
|
||||
if len(slf.items) <= 1 {
|
||||
return
|
||||
}
|
||||
sort.Slice(slf.items, func(i, j int) bool {
|
||||
return slf.items[i].Priority() < slf.items[j].Priority()
|
||||
})
|
||||
for i := 0; i < len(slf.items); i++ {
|
||||
if i == 0 {
|
||||
slf.items[i].prev = nil
|
||||
slf.items[i].next = slf.items[i+1]
|
||||
} else if i == len(slf.items)-1 {
|
||||
slf.items[i].prev = slf.items[i-1]
|
||||
slf.items[i].next = nil
|
||||
} else {
|
||||
slf.items[i].prev = slf.items[i-1]
|
||||
slf.items[i].next = slf.items[i+1]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package slice
|
||||
|
||||
// NewPriorityItem 创建一个优先级切片元素
|
||||
func NewPriorityItem[V any](v V, priority int) *PriorityItem[V] {
|
||||
return &PriorityItem[V]{v: v, p: priority}
|
||||
}
|
||||
|
||||
// PriorityItem 是一个优先级切片元素
|
||||
type PriorityItem[V any] struct {
|
||||
next *PriorityItem[V]
|
||||
prev *PriorityItem[V]
|
||||
v V
|
||||
p int
|
||||
}
|
||||
|
||||
// Value 返回元素值
|
||||
func (p *PriorityItem[V]) Value() V {
|
||||
return p.v
|
||||
}
|
||||
|
||||
// Priority 返回元素优先级
|
||||
func (p *PriorityItem[V]) Priority() int {
|
||||
return p.p
|
||||
}
|
||||
|
||||
// Next 返回下一个元素
|
||||
func (p *PriorityItem[V]) Next() *PriorityItem[V] {
|
||||
return p.next
|
||||
}
|
||||
|
||||
// Prev 返回上一个元素
|
||||
func (p *PriorityItem[V]) Prev() *PriorityItem[V] {
|
||||
return p.prev
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package slice_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPriority_Append(t *testing.T) {
|
||||
var s = slice.NewPriority[string]()
|
||||
s.Append("name_1", 2)
|
||||
s.Append("name_2", 1)
|
||||
fmt.Println(s)
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
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")
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,328 +0,0 @@
|
|||
package slice
|
||||
|
||||
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) {
|
||||
return slice[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetValueHandle 获取特定索引的元素,并通过 handle 进入传入,如果索引超出范围则不会进入 handle
|
||||
func GetValueHandle[V any](slice []V, i int, handle func(v V)) {
|
||||
if i >= 0 && i < len(slice) {
|
||||
handle(slice[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Del 删除特定索引的元素
|
||||
func Del[V any](slice *[]V, index int) {
|
||||
s := *slice
|
||||
*slice = append(s[:index], s[index+1:]...)
|
||||
}
|
||||
|
||||
// Copy 复制特定切片
|
||||
// - 该函数已经可使用标准库 slices.Clone 代替,但是由于调用者可能繁多,所以该函数将不会被移除
|
||||
func Copy[S ~[]V, V any](slice S) S {
|
||||
if slice == nil {
|
||||
return nil
|
||||
}
|
||||
return append(slice[:0:0], slice...)
|
||||
}
|
||||
|
||||
// CopyMatrix 复制二维数组
|
||||
func CopyMatrix[S ~[][]V, V any](slice S) S {
|
||||
if slice == nil {
|
||||
return nil
|
||||
}
|
||||
var result = make(S, len(slice))
|
||||
for i := 0; i < len(slice); i++ {
|
||||
result[i] = Copy(slice[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Insert 在特定索引插入元素
|
||||
func Insert[V any](slice *[]V, index int, value V) {
|
||||
s := *slice
|
||||
if index <= 0 {
|
||||
*slice = append([]V{value}, s...)
|
||||
} else if index >= len(s) {
|
||||
*slice = append(s, value)
|
||||
} else {
|
||||
*slice = append(s[:index], append([]V{value}, s[index:]...)...)
|
||||
}
|
||||
}
|
||||
|
||||
// Move 移动特定索引
|
||||
func Move[V any](slice *[]V, index, to int) {
|
||||
s := *slice
|
||||
v := s[index]
|
||||
if index == to {
|
||||
return
|
||||
} else if to < index {
|
||||
Del[V](slice, index)
|
||||
Insert(slice, to, v)
|
||||
} else {
|
||||
Insert(slice, to, v)
|
||||
Del[V](slice, index)
|
||||
}
|
||||
}
|
||||
|
||||
// NextLoop 返回 i 的下一个数组成员,当 i 达到数组长度时从 0 开始
|
||||
// - 当 i 为 -1 时将返回第一个元素
|
||||
func NextLoop[V any](slice []V, i int) (next int, value V) {
|
||||
if i == -1 {
|
||||
return 0, slice[0]
|
||||
}
|
||||
next = i + 1
|
||||
if next == len(slice) {
|
||||
next = 0
|
||||
}
|
||||
return next, slice[next]
|
||||
}
|
||||
|
||||
// PrevLoop 返回 i 的上一个数组成员,当 i 为 0 时从数组末尾开始
|
||||
// - 当 i 为 -1 时将返回最后一个元素
|
||||
func PrevLoop[V any](slice []V, i int) (prev int, value V) {
|
||||
if i == -1 {
|
||||
return len(slice) - 1, slice[len(slice)-1]
|
||||
}
|
||||
prev = i - 1
|
||||
if prev == -1 {
|
||||
prev = len(slice) - 1
|
||||
}
|
||||
return prev, slice[prev]
|
||||
}
|
||||
|
||||
// Reverse 反转数组
|
||||
func Reverse[V any](slice []V) {
|
||||
for i := 0; i < len(slice)/2; i++ {
|
||||
slice[i], slice[len(slice)-1-i] = slice[len(slice)-1-i], slice[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Distinct 去重
|
||||
func Distinct[V any](slice []V) []V {
|
||||
var result []V
|
||||
for i := range slice {
|
||||
flag := true
|
||||
for j := range result {
|
||||
if reflect.DeepEqual(slice[i], result[j]) {
|
||||
flag = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if flag {
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Swap 交换数组中的两个元素
|
||||
func Swap[V any](slice []V, i, j int) {
|
||||
slice[i], slice[j] = slice[j], slice[i]
|
||||
}
|
||||
|
||||
// ToMap 将数组转换为 map
|
||||
func ToMap[K comparable, V any](slice []V, key func(V) K) map[K]V {
|
||||
m := make(map[K]V)
|
||||
for _, v := range slice {
|
||||
m[key(v)] = v
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// ToSet 将数组转换为 set
|
||||
func ToSet[V comparable](slice []V) map[V]struct{} {
|
||||
m := make(map[V]struct{})
|
||||
for _, v := range slice {
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Merge 合并多个数组
|
||||
func Merge[V any](slices ...[]V) []V {
|
||||
var slice []V
|
||||
for _, s := range slices {
|
||||
slice = append(slice, s...)
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
// GetStartPart 获取数组的前 n 个元素
|
||||
func GetStartPart[V any](slice []V, n int) []V {
|
||||
if n > len(slice) {
|
||||
n = len(slice)
|
||||
}
|
||||
return slice[:n]
|
||||
}
|
||||
|
||||
// GetEndPart 获取数组的后 n 个元素
|
||||
func GetEndPart[V any](slice []V, n int) []V {
|
||||
if n > len(slice) {
|
||||
n = len(slice)
|
||||
}
|
||||
return slice[len(slice)-n:]
|
||||
}
|
||||
|
||||
// GetPart 获取指定区间的元素
|
||||
func GetPart[V any](slice []V, start, end int) []V {
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > len(slice) {
|
||||
end = len(slice)
|
||||
}
|
||||
return slice[start:end]
|
||||
}
|
||||
|
||||
// Contains 判断数组是否包含某个元素
|
||||
func Contains[V comparable](slice []V, value V) bool {
|
||||
for _, v := range slice {
|
||||
if v == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsAny 判断数组是否包含某个元素
|
||||
func ContainsAny[V any](slice []V, values V) bool {
|
||||
for _, v := range slice {
|
||||
if reflect.DeepEqual(v, values) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetIndex 判断数组是否包含某个元素,如果包含则返回索引
|
||||
func GetIndex[V comparable](slice []V, value V) int {
|
||||
for i, v := range slice {
|
||||
if v == value {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// GetIndexAny 判断数组是否包含某个元素,如果包含则返回索引
|
||||
func GetIndexAny[V any](slice []V, values V) int {
|
||||
for i, v := range slice {
|
||||
if reflect.DeepEqual(v, values) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Combinations 获取给定数组的所有组合,包括重复元素的组合
|
||||
func Combinations[T any](a []T) [][]T {
|
||||
var result [][]T
|
||||
n := len(a)
|
||||
for i := 0; i < (1 << n); i++ {
|
||||
var currentCombination []T
|
||||
for j := 0; j < n; j++ {
|
||||
if (i & (1 << j)) != 0 {
|
||||
currentCombination = append(currentCombination, a[j])
|
||||
}
|
||||
}
|
||||
result = append(result, currentCombination)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CombinationsPari 从给定的两个数组中按照特定数量得到所有组合后,再将两个数组的组合进行组合
|
||||
// - 例如从数组 A 中得到所有数量为2的组合,从数组 B 中得到所有数量为3的组合,再将两个组合进行组合,得到所有数量为5的组合
|
||||
func CombinationsPari[T any](a, b []T, aCount, bCount int) [][]T {
|
||||
var combinations [][]T
|
||||
var as, bs = LimitedCombinations(a, aCount, aCount), LimitedCombinations(b, bCount, bCount)
|
||||
for _, asi := range as {
|
||||
for _, bsi := range bs {
|
||||
combinations = append(combinations, append(asi, bsi...))
|
||||
}
|
||||
}
|
||||
return combinations
|
||||
}
|
||||
|
||||
// LimitedCombinations 获取给定数组的所有组合,且每个组合的成员数量限制在指定范围内
|
||||
func LimitedCombinations[T any](a []T, minSize, maxSize int) [][]T {
|
||||
n := len(a)
|
||||
if n == 0 || minSize <= 0 || maxSize <= 0 || minSize > maxSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result [][]T
|
||||
var currentCombination []T
|
||||
|
||||
var backtrack func(startIndex int, currentSize int)
|
||||
backtrack = func(startIndex int, currentSize int) {
|
||||
if currentSize >= minSize && currentSize <= maxSize {
|
||||
combination := make([]T, len(currentCombination))
|
||||
copy(combination, currentCombination)
|
||||
result = append(result, combination)
|
||||
}
|
||||
|
||||
for i := startIndex; i < n; i++ {
|
||||
currentCombination = append(currentCombination, a[i])
|
||||
backtrack(i+1, currentSize+1)
|
||||
currentCombination = currentCombination[:len(currentCombination)-1]
|
||||
}
|
||||
}
|
||||
|
||||
backtrack(0, 0)
|
||||
return result
|
||||
}
|
||||
|
||||
// IsIntersectWithCheck 判断两个切片是否有交集
|
||||
func IsIntersectWithCheck[T any](a, b []T, checkHandle func(a, b T) bool) bool {
|
||||
for _, a := range a {
|
||||
for _, b := range b {
|
||||
if checkHandle(a, b) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIntersect 判断两个切片是否有交集
|
||||
func IsIntersect[T any](a, b []T) bool {
|
||||
for _, a := range a {
|
||||
for _, b := range b {
|
||||
if reflect.DeepEqual(a, b) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SubWithCheck 获取移除指定元素后的切片
|
||||
// - checkHandle 返回 true 表示需要移除
|
||||
func SubWithCheck[T any](a, b []T, checkHandle func(a, b T) bool) []T {
|
||||
var result []T
|
||||
for _, a := range a {
|
||||
flag := false
|
||||
for _, b := range b {
|
||||
if checkHandle(a, b) {
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
result = append(result, a)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package slice_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLimitedCombinations(t *testing.T) {
|
||||
c := slice.LimitedCombinations([]int{1, 2, 3, 4, 5}, 3, 3)
|
||||
for _, v := range c {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue