refactor: 移除 slice 包和 hash 包,新增 listings、mappings 包存放数组、切片、映射等数据结构,原 slice、hash 包中的工具函数迁移至 collection 包,与 sher 包合并并移除 sher 包。完善 collection 包测试用例

This commit is contained in:
kercylan98 2024-01-11 17:50:04 +08:00
parent 3408c212d0
commit 66d903474d
111 changed files with 3538 additions and 3486 deletions

View File

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

View File

@ -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 新的一天事件

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -45,5 +45,5 @@ func TestNewBot(t *testing.T) {
bot.SendPacket([]byte("hello"))
})
srv.Run(":9600")
_ = srv.Run(":9600")
}

View File

@ -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 设置消息数据,该数据将在消息处理完成后释放

View File

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

View File

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

View File

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

View File

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

View File

@ -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 服务器消息

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
package collection_test

View File

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

2
utils/collection/doc.go Normal file
View File

@ -0,0 +1,2 @@
// Package collection 用于对 input 和 map 操作的工具函数
package collection

82
utils/collection/drop.go Normal file
View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package sher
package collection
// DeduplicateSliceInPlace 去除切片中的重复元素
func DeduplicateSliceInPlace[S ~[]V, V comparable](s *S) {

View File

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

View File

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

View File

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

View File

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

View File

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

256
utils/collection/find.go Normal file
View File

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

View File

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

View File

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

6
utils/collection/item.go Normal file
View File

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

View File

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

View File

@ -1,4 +1,4 @@
package slice
package listings
// NewPagedSlice 创建一个新的 PagedSlice 实例。
func NewPagedSlice[T any](pageSize int) *PagedSlice[T] {

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package sher
package collection
// MergeSlices 合并切片
func MergeSlices[S ~[]V, V any](slices ...S) (result S) {

View File

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

View File

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

View File

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

View File

@ -41,7 +41,7 @@ func ZIPUnCompress(dataByte []byte) ([]byte, error) {
if err != nil {
return nil, err
}
rc.Close()
_ = rc.Close()
}
return result.Bytes(), nil
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 改变状态机状态到新的状态

View File

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

View File

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

View File

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

View File

@ -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 将切片转换为 mapvalue作为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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -140,7 +140,7 @@ func UnMergeInt64[V generic.SignedNumber](n int64) (V, V) {
// ToContinuous 将一组非连续的数字转换为从1开始的连续数字
// - 返回值是一个 mapkey 是从 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
}

View File

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

View File

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

View File

@ -1,2 +0,0 @@
// Package sher 用于对 slice 和 map 操作的工具函数,将逐步替代 utils/slice 和 utils/map
package sher

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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