refactor: 移除 slice 包和 hash 包,新增 listings、mappings 包存放数组、切片、映射等数据结构,原 slice、hash 包中的工具函数迁移至 collection 包,与 sher 包合并并移除 sher 包。完善 collection 包测试用例
This commit is contained in:
parent
3408c212d0
commit
66d903474d
|
@ -2,8 +2,8 @@ package activity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"github.com/kercylan98/minotaur/utils/times"
|
"github.com/kercylan98/minotaur/utils/times"
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"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)) {
|
activityEntityDataLoader = append(activityEntityDataLoader, func(handler func(activityType any, activityId any, entityId any, data any)) {
|
||||||
controller.mutex.RLock()
|
controller.mutex.RLock()
|
||||||
entities := hash.Copy(controller.entityData[activityId])
|
entities := collection.CloneMap(controller.entityData[activityId])
|
||||||
controller.mutex.RUnlock()
|
controller.mutex.RUnlock()
|
||||||
for entityId, data := range entities {
|
for entityId, data := range entities {
|
||||||
handler(controller.t, activityId, entityId, data)
|
handler(controller.t, activityId, entityId, data)
|
||||||
|
|
|
@ -2,9 +2,10 @@ package activity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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/generic"
|
||||||
"github.com/kercylan98/minotaur/utils/log"
|
"github.com/kercylan98/minotaur/utils/log"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"github.com/kercylan98/minotaur/utils/timer"
|
"github.com/kercylan98/minotaur/utils/timer"
|
||||||
"github.com/kercylan98/minotaur/utils/times"
|
"github.com/kercylan98/minotaur/utils/times"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -21,28 +22,28 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
upcomingEventHandlers map[any]*slice.Priority[func(activityId any)] // 即将开始的活动事件处理器
|
upcomingEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 即将开始的活动事件处理器
|
||||||
startedEventHandlers map[any]*slice.Priority[func(activityId any)] // 活动开始事件处理器
|
startedEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 活动开始事件处理器
|
||||||
endedEventHandlers map[any]*slice.Priority[func(activityId any)] // 活动结束事件处理器
|
endedEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 活动结束事件处理器
|
||||||
extShowStartedEventHandlers map[any]*slice.Priority[func(activityId any)] // 活动结束后延长展示开始事件处理器
|
extShowStartedEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 活动结束后延长展示开始事件处理器
|
||||||
extShowEndedEventHandlers map[any]*slice.Priority[func(activityId any)] // 活动结束后延长展示结束事件处理器
|
extShowEndedEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 活动结束后延长展示结束事件处理器
|
||||||
newDayEventHandlers map[any]*slice.Priority[func(activityId any)] // 新的一天事件处理器
|
newDayEventHandlers map[any]*listings2.PrioritySlice[func(activityId any)] // 新的一天事件处理器
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
upcomingEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
upcomingEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||||
startedEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
startedEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||||
endedEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
endedEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||||
extShowStartedEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
extShowStartedEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||||
extShowEndedEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
extShowEndedEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||||
newDayEventHandlers = make(map[any]*slice.Priority[func(activityId any)])
|
newDayEventHandlers = make(map[any]*listings2.PrioritySlice[func(activityId any)])
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegUpcomingEvent 注册即将开始的活动事件处理器
|
// RegUpcomingEvent 注册即将开始的活动事件处理器
|
||||||
func RegUpcomingEvent[Type, ID generic.Basic](activityType Type, handler UpcomingEventHandler[ID], priority ...int) {
|
func RegUpcomingEvent[Type, ID generic.Basic](activityType Type, handler UpcomingEventHandler[ID], priority ...int) {
|
||||||
handlers, exist := upcomingEventHandlers[activityType]
|
handlers, exist := upcomingEventHandlers[activityType]
|
||||||
if !exist {
|
if !exist {
|
||||||
handlers = slice.NewPriority[func(activityId any)]()
|
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||||
upcomingEventHandlers[activityType] = handlers
|
upcomingEventHandlers[activityType] = handlers
|
||||||
}
|
}
|
||||||
handlers.Append(func(activityId any) {
|
handlers.Append(func(activityId any) {
|
||||||
|
@ -50,7 +51,7 @@ func RegUpcomingEvent[Type, ID generic.Basic](activityType Type, handler Upcomin
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handler(activityId.(ID))
|
handler(activityId.(ID))
|
||||||
}, slice.GetValue(priority, 0))
|
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnUpcomingEvent 即将开始的活动事件
|
// 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) {
|
func RegStartedEvent[Type, ID generic.Basic](activityType Type, handler StartedEventHandler[ID], priority ...int) {
|
||||||
handlers, exist := startedEventHandlers[activityType]
|
handlers, exist := startedEventHandlers[activityType]
|
||||||
if !exist {
|
if !exist {
|
||||||
handlers = slice.NewPriority[func(activityId any)]()
|
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||||
startedEventHandlers[activityType] = handlers
|
startedEventHandlers[activityType] = handlers
|
||||||
}
|
}
|
||||||
handlers.Append(func(activityId any) {
|
handlers.Append(func(activityId any) {
|
||||||
|
@ -83,7 +84,7 @@ func RegStartedEvent[Type, ID generic.Basic](activityType Type, handler StartedE
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handler(activityId.(ID))
|
handler(activityId.(ID))
|
||||||
}, slice.GetValue(priority, 0))
|
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnStartedEvent 活动开始事件
|
// 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) {
|
func RegEndedEvent[Type, ID generic.Basic](activityType Type, handler EndedEventHandler[ID], priority ...int) {
|
||||||
handlers, exist := endedEventHandlers[activityType]
|
handlers, exist := endedEventHandlers[activityType]
|
||||||
if !exist {
|
if !exist {
|
||||||
handlers = slice.NewPriority[func(activityId any)]()
|
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||||
endedEventHandlers[activityType] = handlers
|
endedEventHandlers[activityType] = handlers
|
||||||
}
|
}
|
||||||
handlers.Append(func(activityId any) {
|
handlers.Append(func(activityId any) {
|
||||||
|
@ -124,7 +125,7 @@ func RegEndedEvent[Type, ID generic.Basic](activityType Type, handler EndedEvent
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handler(activityId.(ID))
|
handler(activityId.(ID))
|
||||||
}, slice.GetValue(priority, 0))
|
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnEndedEvent 活动结束事件
|
// 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) {
|
func RegExtendedShowStartedEvent[Type, ID generic.Basic](activityType Type, handler ExtendedShowStartedEventHandler[ID], priority ...int) {
|
||||||
handlers, exist := extShowStartedEventHandlers[activityType]
|
handlers, exist := extShowStartedEventHandlers[activityType]
|
||||||
if !exist {
|
if !exist {
|
||||||
handlers = slice.NewPriority[func(activityId any)]()
|
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||||
extShowStartedEventHandlers[activityType] = handlers
|
extShowStartedEventHandlers[activityType] = handlers
|
||||||
}
|
}
|
||||||
handlers.Append(func(activityId any) {
|
handlers.Append(func(activityId any) {
|
||||||
|
@ -157,7 +158,7 @@ func RegExtendedShowStartedEvent[Type, ID generic.Basic](activityType Type, hand
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handler(activityId.(ID))
|
handler(activityId.(ID))
|
||||||
}, slice.GetValue(priority, 0))
|
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnExtendedShowStartedEvent 活动结束后延长展示开始事件
|
// 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) {
|
func RegExtendedShowEndedEvent[Type, ID generic.Basic](activityType Type, handler ExtendedShowEndedEventHandler[ID], priority ...int) {
|
||||||
handlers, exist := extShowEndedEventHandlers[activityType]
|
handlers, exist := extShowEndedEventHandlers[activityType]
|
||||||
if !exist {
|
if !exist {
|
||||||
handlers = slice.NewPriority[func(activityId any)]()
|
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||||
extShowEndedEventHandlers[activityType] = handlers
|
extShowEndedEventHandlers[activityType] = handlers
|
||||||
}
|
}
|
||||||
handlers.Append(func(activityId any) {
|
handlers.Append(func(activityId any) {
|
||||||
|
@ -190,7 +191,7 @@ func RegExtendedShowEndedEvent[Type, ID generic.Basic](activityType Type, handle
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handler(activityId.(ID))
|
handler(activityId.(ID))
|
||||||
}, slice.GetValue(priority, 0))
|
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnExtendedShowEndedEvent 活动结束后延长展示结束事件
|
// 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) {
|
func RegNewDayEvent[Type, ID generic.Basic](activityType Type, handler NewDayEventHandler[ID], priority ...int) {
|
||||||
handlers, exist := newDayEventHandlers[activityType]
|
handlers, exist := newDayEventHandlers[activityType]
|
||||||
if !exist {
|
if !exist {
|
||||||
handlers = slice.NewPriority[func(activityId any)]()
|
handlers = listings2.NewPrioritySlice[func(activityId any)]()
|
||||||
newDayEventHandlers[activityType] = handlers
|
newDayEventHandlers[activityType] = handlers
|
||||||
}
|
}
|
||||||
handlers.Append(func(activityId any) {
|
handlers.Append(func(activityId any) {
|
||||||
|
@ -223,7 +224,7 @@ func RegNewDayEvent[Type, ID generic.Basic](activityType Type, handler NewDayEve
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handler(activityId.(ID))
|
handler(activityId.(ID))
|
||||||
}, slice.GetValue(priority, 0))
|
}, collection.FindFirstOrDefaultInSlice(priority, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnNewDayEvent 新的一天事件
|
// OnNewDayEvent 新的一天事件
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package space
|
package space
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -201,7 +200,7 @@ func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetNotEmptySeat() []in
|
||||||
// GetEmptySeat 获取空座位
|
// GetEmptySeat 获取空座位
|
||||||
// - 空座位需要在有对象离开座位后才可能出现
|
// - 空座位需要在有对象离开座位后才可能出现
|
||||||
func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEmptySeat() []int {
|
func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEmptySeat() []int {
|
||||||
return slice.Copy(rc.vacancy)
|
return collection.CloneSlice(rc.vacancy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasSeat 判断是否有座位
|
// 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 {
|
func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntities() map[EntityID]Entity {
|
||||||
rc.entitiesRWMutex.RLock()
|
rc.entitiesRWMutex.RLock()
|
||||||
defer rc.entitiesRWMutex.RUnlock()
|
defer rc.entitiesRWMutex.RUnlock()
|
||||||
return hash.Copy(rc.entities)
|
return collection.CloneMap(rc.entities)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEntity 判断是否有实体
|
// HasEntity 判断是否有实体
|
||||||
|
@ -321,7 +320,7 @@ func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntityExist(id Enti
|
||||||
func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntityIDs() []EntityID {
|
func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntityIDs() []EntityID {
|
||||||
rc.entitiesRWMutex.RLock()
|
rc.entitiesRWMutex.RLock()
|
||||||
defer rc.entitiesRWMutex.RUnlock()
|
defer rc.entitiesRWMutex.RUnlock()
|
||||||
return hash.KeyToSlice(rc.entities)
|
return collection.ConvertMapKeysToSlice(rc.entities)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEntityCount 获取实体数量
|
// GetEntityCount 获取实体数量
|
||||||
|
@ -454,7 +453,7 @@ func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetRoomID() RoomID {
|
||||||
// Broadcast 广播,该函数会将所有房间中满足 conditions 的对象传入 handler 中进行处理
|
// Broadcast 广播,该函数会将所有房间中满足 conditions 的对象传入 handler 中进行处理
|
||||||
func (rc *RoomController[EntityID, RoomID, Entity, Room]) Broadcast(handler func(Entity), conditions ...func(Entity) bool) {
|
func (rc *RoomController[EntityID, RoomID, Entity, Room]) Broadcast(handler func(Entity), conditions ...func(Entity) bool) {
|
||||||
rc.entitiesRWMutex.RLock()
|
rc.entitiesRWMutex.RLock()
|
||||||
entities := hash.Copy(rc.entities)
|
entities := collection.CloneMap(rc.entities)
|
||||||
rc.entitiesRWMutex.RUnlock()
|
rc.entitiesRWMutex.RUnlock()
|
||||||
for _, entity := range entities {
|
for _, entity := range entities {
|
||||||
for _, condition := range conditions {
|
for _, condition := range conditions {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package space
|
package space
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"sync"
|
"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] {
|
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRooms() map[RoomID]*RoomController[EntityID, RoomID, Entity, Room] {
|
||||||
rm.roomsRWMutex.RLock()
|
rm.roomsRWMutex.RLock()
|
||||||
defer rm.roomsRWMutex.RUnlock()
|
defer rm.roomsRWMutex.RUnlock()
|
||||||
return hash.Copy(rm.rooms)
|
return collection.CloneMap(rm.rooms)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRoomCount 获取房间管理器接管的房间数量
|
// GetRoomCount 获取房间管理器接管的房间数量
|
||||||
|
@ -68,13 +68,13 @@ func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomCount() int {
|
||||||
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomIDs() []RoomID {
|
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomIDs() []RoomID {
|
||||||
rm.roomsRWMutex.RLock()
|
rm.roomsRWMutex.RLock()
|
||||||
defer rm.roomsRWMutex.RUnlock()
|
defer rm.roomsRWMutex.RUnlock()
|
||||||
return hash.KeyToSlice(rm.rooms)
|
return collection.ConvertMapKeysToSlice(rm.rooms)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEntity 判断特定对象是否在任一房间中,当对象不在任一房间中时将返回 false
|
// HasEntity 判断特定对象是否在任一房间中,当对象不在任一房间中时将返回 false
|
||||||
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) HasEntity(entityId EntityID) bool {
|
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) HasEntity(entityId EntityID) bool {
|
||||||
rm.roomsRWMutex.RLock()
|
rm.roomsRWMutex.RLock()
|
||||||
rooms := hash.Copy(rm.rooms)
|
rooms := collection.CloneMap(rm.rooms)
|
||||||
rm.roomsRWMutex.RUnlock()
|
rm.roomsRWMutex.RUnlock()
|
||||||
for _, room := range rooms {
|
for _, room := range rooms {
|
||||||
if room.HasEntity(entityId) {
|
if room.HasEntity(entityId) {
|
||||||
|
@ -88,7 +88,7 @@ func (rm *RoomManager[EntityID, RoomID, Entity, Room]) HasEntity(entityId Entity
|
||||||
// - 由于一个对象可能在多个房间中,因此返回值为 map 类型
|
// - 由于一个对象可能在多个房间中,因此返回值为 map 类型
|
||||||
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetEntityRooms(entityId EntityID) map[RoomID]*RoomController[EntityID, RoomID, Entity, Room] {
|
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetEntityRooms(entityId EntityID) map[RoomID]*RoomController[EntityID, RoomID, Entity, Room] {
|
||||||
rm.roomsRWMutex.RLock()
|
rm.roomsRWMutex.RLock()
|
||||||
rooms := hash.Copy(rm.rooms)
|
rooms := collection.CloneMap(rm.rooms)
|
||||||
rm.roomsRWMutex.RUnlock()
|
rm.roomsRWMutex.RUnlock()
|
||||||
var result = make(map[RoomID]*RoomController[EntityID, RoomID, Entity, Room])
|
var result = make(map[RoomID]*RoomController[EntityID, RoomID, Entity, Room])
|
||||||
for id, room := range rooms {
|
for id, room := range rooms {
|
||||||
|
@ -102,7 +102,7 @@ func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetEntityRooms(entityId E
|
||||||
// Broadcast 向所有房间对象广播消息,该方法将会遍历所有房间控制器并调用 RoomController.Broadcast 方法
|
// Broadcast 向所有房间对象广播消息,该方法将会遍历所有房间控制器并调用 RoomController.Broadcast 方法
|
||||||
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) Broadcast(handler func(Entity), conditions ...func(Entity) bool) {
|
func (rm *RoomManager[EntityID, RoomID, Entity, Room]) Broadcast(handler func(Entity), conditions ...func(Entity) bool) {
|
||||||
rm.roomsRWMutex.RLock()
|
rm.roomsRWMutex.RLock()
|
||||||
rooms := hash.Copy(rm.rooms)
|
rooms := collection.CloneMap(rm.rooms)
|
||||||
rm.roomsRWMutex.RUnlock()
|
rm.roomsRWMutex.RUnlock()
|
||||||
for _, room := range rooms {
|
for _, room := range rooms {
|
||||||
room.Broadcast(handler, conditions...)
|
room.Broadcast(handler, conditions...)
|
||||||
|
|
|
@ -37,7 +37,7 @@ func TestCond(t *testing.T) {
|
||||||
|
|
||||||
player := &Player{
|
player := &Player{
|
||||||
tasks: map[string][]*Task{
|
tasks: map[string][]*Task{
|
||||||
task.Type: []*Task{task},
|
task.Type: {task},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
OnRefreshTaskCounterEvent(task.Type, player, 1)
|
OnRefreshTaskCounterEvent(task.Type, player, 1)
|
||||||
|
|
|
@ -5,8 +5,8 @@ import (
|
||||||
"github.com/kercylan98/minotaur/planner/pce"
|
"github.com/kercylan98/minotaur/planner/pce"
|
||||||
"github.com/kercylan98/minotaur/planner/pce/cs"
|
"github.com/kercylan98/minotaur/planner/pce/cs"
|
||||||
"github.com/kercylan98/minotaur/planner/pce/tmpls"
|
"github.com/kercylan98/minotaur/planner/pce/tmpls"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/file"
|
"github.com/kercylan98/minotaur/utils/file"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"github.com/kercylan98/minotaur/utils/str"
|
"github.com/kercylan98/minotaur/utils/str"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/tealeg/xlsx"
|
"github.com/tealeg/xlsx"
|
||||||
|
@ -64,7 +64,7 @@ func init() {
|
||||||
var exporter = pce.NewExporter()
|
var exporter = pce.NewExporter()
|
||||||
loader := pce.NewLoader(pce.GetFields())
|
loader := pce.NewLoader(pce.GetFields())
|
||||||
|
|
||||||
excludes := hash.ToMapBool(str.SplitTrimSpace(exclude, ","))
|
excludes := collection.ConvertSliceToBoolMap(str.SplitTrimSpace(exclude, ","))
|
||||||
for _, xlsxFile := range xlsxFiles {
|
for _, xlsxFile := range xlsxFiles {
|
||||||
xf, err := xlsx.OpenFile(xlsxFile)
|
xf, err := xlsx.OpenFile(xlsxFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"github.com/kercylan98/minotaur/planner/pce"
|
"github.com/kercylan98/minotaur/planner/pce"
|
||||||
"github.com/kercylan98/minotaur/planner/pce/cs"
|
"github.com/kercylan98/minotaur/planner/pce/cs"
|
||||||
"github.com/kercylan98/minotaur/planner/pce/tmpls"
|
"github.com/kercylan98/minotaur/planner/pce/tmpls"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/file"
|
"github.com/kercylan98/minotaur/utils/file"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"github.com/kercylan98/minotaur/utils/str"
|
"github.com/kercylan98/minotaur/utils/str"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/tealeg/xlsx"
|
"github.com/tealeg/xlsx"
|
||||||
|
@ -61,7 +61,7 @@ func init() {
|
||||||
var exporter = pce.NewExporter()
|
var exporter = pce.NewExporter()
|
||||||
loader := pce.NewLoader(pce.GetFields())
|
loader := pce.NewLoader(pce.GetFields())
|
||||||
|
|
||||||
excludes := hash.ToMapBool(str.SplitTrimSpace(exclude, ","))
|
excludes := collection.ConvertSliceToBoolMap(str.SplitTrimSpace(exclude, ","))
|
||||||
for _, xlsxFile := range xlsxFiles {
|
for _, xlsxFile := range xlsxFiles {
|
||||||
xf, err := xlsx.OpenFile(xlsxFile)
|
xf, err := xlsx.OpenFile(xlsxFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"github.com/kercylan98/minotaur/planner/pce"
|
"github.com/kercylan98/minotaur/planner/pce"
|
||||||
"github.com/kercylan98/minotaur/planner/pce/cs"
|
"github.com/kercylan98/minotaur/planner/pce/cs"
|
||||||
"github.com/kercylan98/minotaur/planner/pce/tmpls"
|
"github.com/kercylan98/minotaur/planner/pce/tmpls"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/file"
|
"github.com/kercylan98/minotaur/utils/file"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"github.com/kercylan98/minotaur/utils/str"
|
"github.com/kercylan98/minotaur/utils/str"
|
||||||
"github.com/tealeg/xlsx"
|
"github.com/tealeg/xlsx"
|
||||||
"os"
|
"os"
|
||||||
|
@ -61,7 +61,7 @@ func TestExecute(t *testing.T) {
|
||||||
var exporter = pce.NewExporter()
|
var exporter = pce.NewExporter()
|
||||||
loader := pce.NewLoader(pce.GetFields())
|
loader := pce.NewLoader(pce.GetFields())
|
||||||
|
|
||||||
excludes := hash.ToMapBool(str.SplitTrimSpace(exclude, ","))
|
excludes := collection.ConvertSliceToBoolMap(str.SplitTrimSpace(exclude, ","))
|
||||||
for _, xlsxFile := range xlsxFiles {
|
for _, xlsxFile := range xlsxFiles {
|
||||||
xf, err := xlsx.OpenFile(xlsxFile)
|
xf, err := xlsx.OpenFile(xlsxFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -2,7 +2,7 @@ package pce
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/str"
|
"github.com/kercylan98/minotaur/utils/str"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -96,7 +96,7 @@ func (slf *TmplField) handleSlice(fieldName, fieldType string, fields map[string
|
||||||
}
|
}
|
||||||
slf.slice = true
|
slf.slice = true
|
||||||
t := strings.TrimPrefix(fieldType, "[]")
|
t := strings.TrimPrefix(fieldType, "[]")
|
||||||
if hash.Exist(fields, t) {
|
if collection.FindInMapKey(fields, t) {
|
||||||
slf.Struct = nil
|
slf.Struct = nil
|
||||||
slf.Type = t
|
slf.Type = t
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package pce
|
package pce
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TmplStruct 模板结构
|
// TmplStruct 模板结构
|
||||||
|
@ -19,7 +19,7 @@ func (slf *TmplStruct) addField(parent, name, desc, fieldType string, fields map
|
||||||
Desc: desc,
|
Desc: desc,
|
||||||
Type: fieldType,
|
Type: fieldType,
|
||||||
}
|
}
|
||||||
if !hash.Exist(fields, fieldType) {
|
if !collection.FindInMapKey(fields, fieldType) {
|
||||||
field.setStruct(parent, name, desc, fieldType, fields)
|
field.setStruct(parent, name, desc, fieldType, fields)
|
||||||
} else {
|
} else {
|
||||||
field.Type = GetFieldGolangType(fields[fieldType])
|
field.Type = GetFieldGolangType(fields[fieldType])
|
||||||
|
|
|
@ -45,5 +45,5 @@ func TestNewBot(t *testing.T) {
|
||||||
bot.SendPacket([]byte("hello"))
|
bot.SendPacket([]byte("hello"))
|
||||||
})
|
})
|
||||||
|
|
||||||
srv.Run(":9600")
|
_ = srv.Run(":9600")
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/kercylan98/minotaur/server/writeloop"
|
"github.com/kercylan98/minotaur/server/writeloop"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/concurrent"
|
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"github.com/kercylan98/minotaur/utils/log"
|
"github.com/kercylan98/minotaur/utils/log"
|
||||||
"github.com/kercylan98/minotaur/utils/random"
|
"github.com/kercylan98/minotaur/utils/random"
|
||||||
"github.com/kercylan98/minotaur/utils/timer"
|
"github.com/kercylan98/minotaur/utils/timer"
|
||||||
|
@ -203,7 +203,7 @@ func (slf *Conn) GetData(key any) any {
|
||||||
|
|
||||||
// ViewData 查看只读的连接数据
|
// ViewData 查看只读的连接数据
|
||||||
func (slf *Conn) ViewData() map[any]any {
|
func (slf *Conn) ViewData() map[any]any {
|
||||||
return hash.Copy(slf.data)
|
return collection.CloneMap(slf.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMessageData 设置消息数据,该数据将在消息处理完成后释放
|
// SetMessageData 设置消息数据,该数据将在消息处理完成后释放
|
||||||
|
|
107
server/event.go
107
server/event.go
|
@ -2,9 +2,10 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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/log"
|
||||||
"github.com/kercylan98/minotaur/utils/runtimes"
|
"github.com/kercylan98/minotaur/utils/runtimes"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -42,51 +43,51 @@ type (
|
||||||
func newEvent(srv *Server) *event {
|
func newEvent(srv *Server) *event {
|
||||||
return &event{
|
return &event{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
startBeforeEventHandlers: slice.NewPriority[StartBeforeEventHandler](),
|
startBeforeEventHandlers: listings2.NewPrioritySlice[StartBeforeEventHandler](),
|
||||||
startFinishEventHandlers: slice.NewPriority[StartFinishEventHandler](),
|
startFinishEventHandlers: listings2.NewPrioritySlice[StartFinishEventHandler](),
|
||||||
stopEventHandlers: slice.NewPriority[StopEventHandler](),
|
stopEventHandlers: listings2.NewPrioritySlice[StopEventHandler](),
|
||||||
connectionReceivePacketEventHandlers: slice.NewPriority[ConnectionReceivePacketEventHandler](),
|
connectionReceivePacketEventHandlers: listings2.NewPrioritySlice[ConnectionReceivePacketEventHandler](),
|
||||||
connectionOpenedEventHandlers: slice.NewPriority[ConnectionOpenedEventHandler](),
|
connectionOpenedEventHandlers: listings2.NewPrioritySlice[ConnectionOpenedEventHandler](),
|
||||||
connectionClosedEventHandlers: slice.NewPriority[ConnectionClosedEventHandler](),
|
connectionClosedEventHandlers: listings2.NewPrioritySlice[ConnectionClosedEventHandler](),
|
||||||
messageErrorEventHandlers: slice.NewPriority[MessageErrorEventHandler](),
|
messageErrorEventHandlers: listings2.NewPrioritySlice[MessageErrorEventHandler](),
|
||||||
messageLowExecEventHandlers: slice.NewPriority[MessageLowExecEventHandler](),
|
messageLowExecEventHandlers: listings2.NewPrioritySlice[MessageLowExecEventHandler](),
|
||||||
connectionOpenedAfterEventHandlers: slice.NewPriority[ConnectionOpenedAfterEventHandler](),
|
connectionOpenedAfterEventHandlers: listings2.NewPrioritySlice[ConnectionOpenedAfterEventHandler](),
|
||||||
connectionWritePacketBeforeHandlers: slice.NewPriority[ConnectionWritePacketBeforeEventHandler](),
|
connectionWritePacketBeforeHandlers: listings2.NewPrioritySlice[ConnectionWritePacketBeforeEventHandler](),
|
||||||
shuntChannelCreatedEventHandlers: slice.NewPriority[ShuntChannelCreatedEventHandler](),
|
shuntChannelCreatedEventHandlers: listings2.NewPrioritySlice[ShuntChannelCreatedEventHandler](),
|
||||||
shuntChannelClosedEventHandlers: slice.NewPriority[ShuntChannelClosedEventHandler](),
|
shuntChannelClosedEventHandlers: listings2.NewPrioritySlice[ShuntChannelClosedEventHandler](),
|
||||||
connectionPacketPreprocessEventHandlers: slice.NewPriority[ConnectionPacketPreprocessEventHandler](),
|
connectionPacketPreprocessEventHandlers: listings2.NewPrioritySlice[ConnectionPacketPreprocessEventHandler](),
|
||||||
messageExecBeforeEventHandlers: slice.NewPriority[MessageExecBeforeEventHandler](),
|
messageExecBeforeEventHandlers: listings2.NewPrioritySlice[MessageExecBeforeEventHandler](),
|
||||||
messageReadyEventHandlers: slice.NewPriority[MessageReadyEventHandler](),
|
messageReadyEventHandlers: listings2.NewPrioritySlice[MessageReadyEventHandler](),
|
||||||
deadlockDetectEventHandlers: slice.NewPriority[OnDeadlockDetectEventHandler](),
|
deadlockDetectEventHandlers: listings2.NewPrioritySlice[OnDeadlockDetectEventHandler](),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type event struct {
|
type event struct {
|
||||||
*Server
|
*Server
|
||||||
startBeforeEventHandlers *slice.Priority[StartBeforeEventHandler]
|
startBeforeEventHandlers *listings2.PrioritySlice[StartBeforeEventHandler]
|
||||||
startFinishEventHandlers *slice.Priority[StartFinishEventHandler]
|
startFinishEventHandlers *listings2.PrioritySlice[StartFinishEventHandler]
|
||||||
stopEventHandlers *slice.Priority[StopEventHandler]
|
stopEventHandlers *listings2.PrioritySlice[StopEventHandler]
|
||||||
connectionReceivePacketEventHandlers *slice.Priority[ConnectionReceivePacketEventHandler]
|
connectionReceivePacketEventHandlers *listings2.PrioritySlice[ConnectionReceivePacketEventHandler]
|
||||||
connectionOpenedEventHandlers *slice.Priority[ConnectionOpenedEventHandler]
|
connectionOpenedEventHandlers *listings2.PrioritySlice[ConnectionOpenedEventHandler]
|
||||||
connectionClosedEventHandlers *slice.Priority[ConnectionClosedEventHandler]
|
connectionClosedEventHandlers *listings2.PrioritySlice[ConnectionClosedEventHandler]
|
||||||
messageErrorEventHandlers *slice.Priority[MessageErrorEventHandler]
|
messageErrorEventHandlers *listings2.PrioritySlice[MessageErrorEventHandler]
|
||||||
messageLowExecEventHandlers *slice.Priority[MessageLowExecEventHandler]
|
messageLowExecEventHandlers *listings2.PrioritySlice[MessageLowExecEventHandler]
|
||||||
connectionOpenedAfterEventHandlers *slice.Priority[ConnectionOpenedAfterEventHandler]
|
connectionOpenedAfterEventHandlers *listings2.PrioritySlice[ConnectionOpenedAfterEventHandler]
|
||||||
connectionWritePacketBeforeHandlers *slice.Priority[ConnectionWritePacketBeforeEventHandler]
|
connectionWritePacketBeforeHandlers *listings2.PrioritySlice[ConnectionWritePacketBeforeEventHandler]
|
||||||
shuntChannelCreatedEventHandlers *slice.Priority[ShuntChannelCreatedEventHandler]
|
shuntChannelCreatedEventHandlers *listings2.PrioritySlice[ShuntChannelCreatedEventHandler]
|
||||||
shuntChannelClosedEventHandlers *slice.Priority[ShuntChannelClosedEventHandler]
|
shuntChannelClosedEventHandlers *listings2.PrioritySlice[ShuntChannelClosedEventHandler]
|
||||||
connectionPacketPreprocessEventHandlers *slice.Priority[ConnectionPacketPreprocessEventHandler]
|
connectionPacketPreprocessEventHandlers *listings2.PrioritySlice[ConnectionPacketPreprocessEventHandler]
|
||||||
messageExecBeforeEventHandlers *slice.Priority[MessageExecBeforeEventHandler]
|
messageExecBeforeEventHandlers *listings2.PrioritySlice[MessageExecBeforeEventHandler]
|
||||||
messageReadyEventHandlers *slice.Priority[MessageReadyEventHandler]
|
messageReadyEventHandlers *listings2.PrioritySlice[MessageReadyEventHandler]
|
||||||
deadlockDetectEventHandlers *slice.Priority[OnDeadlockDetectEventHandler]
|
deadlockDetectEventHandlers *listings2.PrioritySlice[OnDeadlockDetectEventHandler]
|
||||||
|
|
||||||
consoleCommandEventHandlers map[string]*slice.Priority[ConsoleCommandEventHandler]
|
consoleCommandEventHandlers map[string]*listings2.PrioritySlice[ConsoleCommandEventHandler]
|
||||||
consoleCommandEventHandlerInitOnce sync.Once
|
consoleCommandEventHandlerInitOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegStopEvent 服务器停止时将立即执行被注册的事件处理函数
|
// RegStopEvent 服务器停止时将立即执行被注册的事件处理函数
|
||||||
func (slf *event) RegStopEvent(handler StopEventHandler, priority ...int) {
|
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()))
|
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.consoleCommandEventHandlerInitOnce.Do(func() {
|
||||||
slf.consoleCommandEventHandlers = map[string]*slice.Priority[ConsoleCommandEventHandler]{}
|
slf.consoleCommandEventHandlers = map[string]*listings2.PrioritySlice[ConsoleCommandEventHandler]{}
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
var input string
|
var input string
|
||||||
|
@ -123,10 +124,10 @@ func (slf *event) RegConsoleCommandEvent(command string, handler ConsoleCommandE
|
||||||
})
|
})
|
||||||
list, exist := slf.consoleCommandEventHandlers[command]
|
list, exist := slf.consoleCommandEventHandlers[command]
|
||||||
if !exist {
|
if !exist {
|
||||||
list = slice.NewPriority[ConsoleCommandEventHandler]()
|
list = listings2.NewPrioritySlice[ConsoleCommandEventHandler]()
|
||||||
slf.consoleCommandEventHandlers[command] = list
|
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()))
|
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 在服务器初始化完成启动前立刻执行被注册的事件处理函数
|
// RegStartBeforeEvent 在服务器初始化完成启动前立刻执行被注册的事件处理函数
|
||||||
func (slf *event) RegStartBeforeEvent(handler StartBeforeEventHandler, priority ...int) {
|
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()))
|
log.Info("Server", log.String("RegEvent", runtimes.CurrentRunningFuncName()), log.String("handler", reflect.TypeOf(handler).String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +182,7 @@ func (slf *event) OnStartBeforeEvent() {
|
||||||
// RegStartFinishEvent 在服务器启动完成时将立刻执行被注册的事件处理函数
|
// RegStartFinishEvent 在服务器启动完成时将立刻执行被注册的事件处理函数
|
||||||
// - 需要注意该时刻服务器已经启动完成,但是还有可能未开始处理消息,客户端有可能无法连接,如果需要在消息处理器准备就绪后执行,请使用 RegMessageReadyEvent 函数
|
// - 需要注意该时刻服务器已经启动完成,但是还有可能未开始处理消息,客户端有可能无法连接,如果需要在消息处理器准备就绪后执行,请使用 RegMessageReadyEvent 函数
|
||||||
func (slf *event) RegStartFinishEvent(handler StartFinishEventHandler, priority ...int) {
|
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()))
|
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 {
|
if slf.network == NetworkHttp {
|
||||||
panic(ErrNetworkIncompatibleHttp)
|
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()))
|
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 {
|
if slf.network == NetworkHttp {
|
||||||
panic(ErrNetworkIncompatibleHttp)
|
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()))
|
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 {
|
if slf.network == NetworkHttp {
|
||||||
panic(ErrNetworkIncompatibleHttp)
|
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()))
|
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 在处理消息发生错误时将立即执行被注册的事件处理函数
|
// RegMessageErrorEvent 在处理消息发生错误时将立即执行被注册的事件处理函数
|
||||||
func (slf *event) RegMessageErrorEvent(handler MessageErrorEventHandler, priority ...int) {
|
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()))
|
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 在处理消息缓慢时将立即执行被注册的事件处理函数
|
// RegMessageLowExecEvent 在处理消息缓慢时将立即执行被注册的事件处理函数
|
||||||
func (slf *event) RegMessageLowExecEvent(handler MessageLowExecEventHandler, priority ...int) {
|
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()))
|
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 {
|
if slf.network == NetworkHttp {
|
||||||
panic(ErrNetworkIncompatibleHttp)
|
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()))
|
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 {
|
if slf.network == NetworkHttp {
|
||||||
panic(ErrNetworkIncompatibleHttp)
|
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()))
|
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 在分流通道创建时将立刻执行被注册的事件处理函数
|
// RegShuntChannelCreatedEvent 在分流通道创建时将立刻执行被注册的事件处理函数
|
||||||
func (slf *event) RegShuntChannelCreatedEvent(handler ShuntChannelCreatedEventHandler, priority ...int) {
|
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()))
|
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 在分流通道关闭时将立刻执行被注册的事件处理函数
|
// RegShuntChannelCloseEvent 在分流通道关闭时将立刻执行被注册的事件处理函数
|
||||||
func (slf *event) RegShuntChannelCloseEvent(handler ShuntChannelClosedEventHandler, priority ...int) {
|
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()))
|
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) {
|
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()))
|
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) {
|
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()))
|
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 在服务器消息处理器准备就绪时立即执行被注册的事件处理函数
|
// RegMessageReadyEvent 在服务器消息处理器准备就绪时立即执行被注册的事件处理函数
|
||||||
func (slf *event) RegMessageReadyEvent(handler MessageReadyEventHandler, priority ...int) {
|
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() {
|
func (slf *event) OnMessageReadyEvent() {
|
||||||
|
@ -447,7 +448,7 @@ func (slf *event) OnMessageReadyEvent() {
|
||||||
|
|
||||||
// RegDeadlockDetectEvent 在死锁检测触发时立即执行被注册的事件处理函数
|
// RegDeadlockDetectEvent 在死锁检测触发时立即执行被注册的事件处理函数
|
||||||
func (slf *event) RegDeadlockDetectEvent(handler OnDeadlockDetectEventHandler, priority ...int) {
|
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) {
|
func (slf *event) OnDeadlockDetectEvent(message *Message) {
|
||||||
|
|
|
@ -2,7 +2,8 @@ package gateway
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/server"
|
"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 (
|
type (
|
||||||
|
@ -16,27 +17,27 @@ type (
|
||||||
|
|
||||||
func newEvents() *events {
|
func newEvents() *events {
|
||||||
return &events{
|
return &events{
|
||||||
connectionOpenedEventHandles: slice.NewPriority[ConnectionOpenedEventHandle](),
|
connectionOpenedEventHandles: listings2.NewPrioritySlice[ConnectionOpenedEventHandle](),
|
||||||
connectionClosedEventHandles: slice.NewPriority[ConnectionClosedEventHandle](),
|
connectionClosedEventHandles: listings2.NewPrioritySlice[ConnectionClosedEventHandle](),
|
||||||
connectionReceivePacketEventHandles: slice.NewPriority[ConnectionReceivePacketEventHandle](),
|
connectionReceivePacketEventHandles: listings2.NewPrioritySlice[ConnectionReceivePacketEventHandle](),
|
||||||
endpointConnectOpenedEventHandles: slice.NewPriority[EndpointConnectOpenedEventHandle](),
|
endpointConnectOpenedEventHandles: listings2.NewPrioritySlice[EndpointConnectOpenedEventHandle](),
|
||||||
endpointConnectClosedEventHandles: slice.NewPriority[EndpointConnectClosedEventHandle](),
|
endpointConnectClosedEventHandles: listings2.NewPrioritySlice[EndpointConnectClosedEventHandle](),
|
||||||
endpointConnectReceivePacketEventHandles: slice.NewPriority[EndpointConnectReceivePacketEventHandle](),
|
endpointConnectReceivePacketEventHandles: listings2.NewPrioritySlice[EndpointConnectReceivePacketEventHandle](),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type events struct {
|
type events struct {
|
||||||
connectionOpenedEventHandles *slice.Priority[ConnectionOpenedEventHandle]
|
connectionOpenedEventHandles *listings2.PrioritySlice[ConnectionOpenedEventHandle]
|
||||||
connectionClosedEventHandles *slice.Priority[ConnectionClosedEventHandle]
|
connectionClosedEventHandles *listings2.PrioritySlice[ConnectionClosedEventHandle]
|
||||||
connectionReceivePacketEventHandles *slice.Priority[ConnectionReceivePacketEventHandle]
|
connectionReceivePacketEventHandles *listings2.PrioritySlice[ConnectionReceivePacketEventHandle]
|
||||||
endpointConnectOpenedEventHandles *slice.Priority[EndpointConnectOpenedEventHandle]
|
endpointConnectOpenedEventHandles *listings2.PrioritySlice[EndpointConnectOpenedEventHandle]
|
||||||
endpointConnectClosedEventHandles *slice.Priority[EndpointConnectClosedEventHandle]
|
endpointConnectClosedEventHandles *listings2.PrioritySlice[EndpointConnectClosedEventHandle]
|
||||||
endpointConnectReceivePacketEventHandles *slice.Priority[EndpointConnectReceivePacketEventHandle]
|
endpointConnectReceivePacketEventHandles *listings2.PrioritySlice[EndpointConnectReceivePacketEventHandle]
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegConnectionOpenedEventHandle 注册客户端连接打开事件处理函数
|
// RegConnectionOpenedEventHandle 注册客户端连接打开事件处理函数
|
||||||
func (slf *events) RegConnectionOpenedEventHandle(handle ConnectionOpenedEventHandle, priority ...int) {
|
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) {
|
func (slf *events) OnConnectionOpenedEvent(gateway *Gateway, conn *server.Conn) {
|
||||||
|
@ -48,7 +49,7 @@ func (slf *events) OnConnectionOpenedEvent(gateway *Gateway, conn *server.Conn)
|
||||||
|
|
||||||
// RegConnectionClosedEventHandle 注册客户端连接关闭事件处理函数
|
// RegConnectionClosedEventHandle 注册客户端连接关闭事件处理函数
|
||||||
func (slf *events) RegConnectionClosedEventHandle(handle ConnectionClosedEventHandle, priority ...int) {
|
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) {
|
func (slf *events) OnConnectionClosedEvent(gateway *Gateway, conn *server.Conn) {
|
||||||
|
@ -60,7 +61,7 @@ func (slf *events) OnConnectionClosedEvent(gateway *Gateway, conn *server.Conn)
|
||||||
|
|
||||||
// RegConnectionReceivePacketEventHandle 注册客户端连接接收数据包事件处理函数
|
// RegConnectionReceivePacketEventHandle 注册客户端连接接收数据包事件处理函数
|
||||||
func (slf *events) RegConnectionReceivePacketEventHandle(handle ConnectionReceivePacketEventHandle, priority ...int) {
|
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) {
|
func (slf *events) OnConnectionReceivePacketEvent(gateway *Gateway, conn *server.Conn, packet []byte) {
|
||||||
|
@ -72,7 +73,7 @@ func (slf *events) OnConnectionReceivePacketEvent(gateway *Gateway, conn *server
|
||||||
|
|
||||||
// RegEndpointConnectOpenedEventHandle 注册端点连接打开事件处理函数
|
// RegEndpointConnectOpenedEventHandle 注册端点连接打开事件处理函数
|
||||||
func (slf *events) RegEndpointConnectOpenedEventHandle(handle EndpointConnectOpenedEventHandle, priority ...int) {
|
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) {
|
func (slf *events) OnEndpointConnectOpenedEvent(gateway *Gateway, endpoint *Endpoint) {
|
||||||
|
@ -84,7 +85,7 @@ func (slf *events) OnEndpointConnectOpenedEvent(gateway *Gateway, endpoint *Endp
|
||||||
|
|
||||||
// RegEndpointConnectClosedEventHandle 注册端点连接关闭事件处理函数
|
// RegEndpointConnectClosedEventHandle 注册端点连接关闭事件处理函数
|
||||||
func (slf *events) RegEndpointConnectClosedEventHandle(handle EndpointConnectClosedEventHandle, priority ...int) {
|
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) {
|
func (slf *events) OnEndpointConnectClosedEvent(gateway *Gateway, endpoint *Endpoint) {
|
||||||
|
@ -96,7 +97,7 @@ func (slf *events) OnEndpointConnectClosedEvent(gateway *Gateway, endpoint *Endp
|
||||||
|
|
||||||
// RegEndpointConnectReceivePacketEventHandle 注册端点连接接收数据包事件处理函数
|
// RegEndpointConnectReceivePacketEventHandle 注册端点连接接收数据包事件处理函数
|
||||||
func (slf *events) RegEndpointConnectReceivePacketEventHandle(handle EndpointConnectReceivePacketEventHandle, priority ...int) {
|
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) {
|
func (slf *events) OnEndpointConnectReceivePacketEvent(gateway *Gateway, endpoint *Endpoint, conn *server.Conn, packet []byte) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ func (h *hub) IsOnline(id string) bool {
|
||||||
// GetOnlineAll 获取所有在线连接
|
// GetOnlineAll 获取所有在线连接
|
||||||
func (h *hub) GetOnlineAll() map[string]*Conn {
|
func (h *hub) GetOnlineAll() map[string]*Conn {
|
||||||
h.chanMutex.RLock()
|
h.chanMutex.RLock()
|
||||||
cop := hash.Copy(h.connections)
|
cop := collection.CloneMap(h.connections)
|
||||||
h.chanMutex.RUnlock()
|
h.chanMutex.RUnlock()
|
||||||
return cop
|
return cop
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ func (slf *Lockstep[ClientID, Command]) GetClientCount() int {
|
||||||
func (slf *Lockstep[ClientID, Command]) DropCache(handler func(frame int64) bool) {
|
func (slf *Lockstep[ClientID, Command]) DropCache(handler func(frame int64) bool) {
|
||||||
slf.frameCacheLock.Lock()
|
slf.frameCacheLock.Lock()
|
||||||
defer slf.frameCacheLock.Unlock()
|
defer slf.frameCacheLock.Unlock()
|
||||||
for frame, _ := range slf.frameCache {
|
for frame := range slf.frameCache {
|
||||||
if handler(frame) {
|
if handler(frame) {
|
||||||
delete(slf.frameCache, frame)
|
delete(slf.frameCache, frame)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/log"
|
"github.com/kercylan98/minotaur/utils/log"
|
||||||
"github.com/kercylan98/minotaur/utils/super"
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
)
|
)
|
||||||
|
@ -70,7 +70,7 @@ type (
|
||||||
|
|
||||||
// HasMessageType 检查是否存在指定的消息类型
|
// HasMessageType 检查是否存在指定的消息类型
|
||||||
func HasMessageType(mt MessageType) bool {
|
func HasMessageType(mt MessageType) bool {
|
||||||
return hash.Exist(messageNames, mt)
|
return collection.FindInMapKey(messageNames, mt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message 服务器消息
|
// Message 服务器消息
|
||||||
|
|
|
@ -4,9 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/kercylan98/minotaur/server/internal/logger"
|
"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/log"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"github.com/kercylan98/minotaur/utils/super"
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
"github.com/panjf2000/gnet"
|
"github.com/panjf2000/gnet"
|
||||||
"github.com/xtaci/kcp-go/v5"
|
"github.com/xtaci/kcp-go/v5"
|
||||||
|
@ -53,12 +52,12 @@ func init() {
|
||||||
|
|
||||||
// GetNetworks 获取所有支持的网络模式
|
// GetNetworks 获取所有支持的网络模式
|
||||||
func GetNetworks() []Network {
|
func GetNetworks() []Network {
|
||||||
return slice.Copy(networks)
|
return collection.CloneSlice(networks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check 检查网络模式是否支持
|
// check 检查网络模式是否支持
|
||||||
func (n Network) 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))
|
panic(fmt.Errorf("unsupported network mode: %s", n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,10 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/kercylan98/minotaur/server/internal/dispatcher"
|
"github.com/kercylan98/minotaur/server/internal/dispatcher"
|
||||||
"github.com/kercylan98/minotaur/server/internal/logger"
|
"github.com/kercylan98/minotaur/server/internal/logger"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/concurrent"
|
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||||
"github.com/kercylan98/minotaur/utils/log"
|
"github.com/kercylan98/minotaur/utils/log"
|
||||||
"github.com/kercylan98/minotaur/utils/network"
|
"github.com/kercylan98/minotaur/utils/network"
|
||||||
"github.com/kercylan98/minotaur/utils/sher"
|
|
||||||
"github.com/kercylan98/minotaur/utils/str"
|
"github.com/kercylan98/minotaur/utils/str"
|
||||||
"github.com/kercylan98/minotaur/utils/super"
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
"github.com/kercylan98/minotaur/utils/timer"
|
"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, log.String("type", messageNames[message.t]), log.String("cost", cost.String()), log.String("message", message.String()))
|
||||||
fields = append(fields, message.marks...)
|
fields = append(fields, message.marks...)
|
||||||
//fields = append(fields, log.Stack("stack"))
|
//fields = append(fields, log.Stack("stack"))
|
||||||
log.Warn("ServerLowMessage", sher.ConvertSliceToAny(fields)...)
|
log.Warn("ServerLowMessage", collection.ConvertSliceToAny(fields)...)
|
||||||
srv.OnMessageLowExecEvent(message, cost)
|
srv.OnMessageLowExecEvent(message, cost)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package aoi
|
package aoi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
"github.com/kercylan98/minotaur/utils/geometry"
|
"github.com/kercylan98/minotaur/utils/geometry"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"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 {
|
func (slf *TwoDimensional[EID, PosType, E]) GetFocus(id EID) map[EID]E {
|
||||||
slf.rw.RLock()
|
slf.rw.RLock()
|
||||||
defer slf.rw.RUnlock()
|
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) {
|
func (slf *TwoDimensional[EID, PosType, E]) SetSize(width, height int) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package arrangement
|
package arrangement
|
||||||
|
|
||||||
import "github.com/kercylan98/minotaur/utils/hash"
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
)
|
||||||
|
|
||||||
// Area 编排区域
|
// Area 编排区域
|
||||||
type Area[ID comparable, AreaInfo any] struct {
|
type Area[ID comparable, AreaInfo any] struct {
|
||||||
|
@ -43,7 +45,7 @@ func (slf *Area[ID, AreaInfo]) IsAllow(item Item[ID]) (constraintErr error, conf
|
||||||
|
|
||||||
// IsConflict 检测一个成员是否会造成冲突
|
// IsConflict 检测一个成员是否会造成冲突
|
||||||
func (slf *Area[ID, AreaInfo]) IsConflict(item Item[ID]) bool {
|
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
|
return false
|
||||||
}
|
}
|
||||||
for _, conflict := range slf.conflicts {
|
for _, conflict := range slf.conflicts {
|
||||||
|
@ -56,7 +58,7 @@ func (slf *Area[ID, AreaInfo]) IsConflict(item Item[ID]) bool {
|
||||||
|
|
||||||
// GetConflictItems 获取与一个成员产生冲突的所有其他成员
|
// GetConflictItems 获取与一个成员产生冲突的所有其他成员
|
||||||
func (slf *Area[ID, AreaInfo]) GetConflictItems(item Item[ID]) map[ID]Item[ID] {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
var conflictItems map[ID]Item[ID]
|
var conflictItems map[ID]Item[ID]
|
||||||
|
@ -79,7 +81,7 @@ func (slf *Area[ID, AreaInfo]) GetScore(extra ...Item[ID]) float64 {
|
||||||
if slf.evaluate == nil {
|
if slf.evaluate == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
var items = hash.Copy(slf.items)
|
var items = collection.CloneMap(slf.items)
|
||||||
for _, item := range extra {
|
for _, item := range extra {
|
||||||
items[item.GetID()] = item
|
items[item.GetID()] = item
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package arrangement
|
package arrangement
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -59,8 +59,8 @@ func (slf *Arrangement[ID, AreaInfo]) Arrange() (areas []*Area[ID, AreaInfo], no
|
||||||
return slf.areas, slf.items
|
return slf.areas, slf.items
|
||||||
}
|
}
|
||||||
|
|
||||||
var items = hash.Copy(slf.items)
|
var items = collection.CloneMap(slf.items)
|
||||||
var fixed = hash.Copy(slf.fixed)
|
var fixed = collection.CloneMap(slf.fixed)
|
||||||
|
|
||||||
// 将固定编排的成员添加到对应的编排区域中,当成员无法添加到对应的编排区域中时,将会被转移至未编排区域
|
// 将固定编排的成员添加到对应的编排区域中,当成员无法添加到对应的编排区域中时,将会被转移至未编排区域
|
||||||
for id, isFixed := range 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]{
|
var editor = &Editor[ID, AreaInfo]{
|
||||||
a: slf,
|
a: slf,
|
||||||
pending: hash.ToSlice(items),
|
pending: collection.ConvertMapValuesToSlice(items),
|
||||||
falls: map[ID]struct{}{},
|
falls: map[ID]struct{}{},
|
||||||
}
|
}
|
||||||
sort.Slice(editor.pending, func(i, j int) bool {
|
sort.Slice(editor.pending, func(i, j int) bool {
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package arrangement
|
package arrangement
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,7 +32,7 @@ func (slf *Editor[ID, AreaInfo]) RemoveAreaItem(area *Area[ID, AreaInfo], item I
|
||||||
|
|
||||||
// AddAreaItem 将一个成员添加到编排区域中,如果该成员已经存在于编排区域中,则不进行任何操作
|
// AddAreaItem 将一个成员添加到编排区域中,如果该成员已经存在于编排区域中,则不进行任何操作
|
||||||
func (slf *Editor[ID, AreaInfo]) AddAreaItem(area *Area[ID, AreaInfo], item Item[ID]) {
|
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
|
return
|
||||||
}
|
}
|
||||||
area.items[item.GetID()] = item
|
area.items[item.GetID()] = item
|
||||||
|
@ -42,12 +41,12 @@ func (slf *Editor[ID, AreaInfo]) AddAreaItem(area *Area[ID, AreaInfo], item Item
|
||||||
|
|
||||||
// GetAreas 获取所有的编排区域
|
// GetAreas 获取所有的编排区域
|
||||||
func (slf *Editor[ID, AreaInfo]) GetAreas() []*Area[ID, AreaInfo] {
|
func (slf *Editor[ID, AreaInfo]) GetAreas() []*Area[ID, AreaInfo] {
|
||||||
return slice.Copy(slf.a.areas)
|
return collection.CloneSlice(slf.a.areas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAreasWithScoreAsc 获取所有的编排区域,并按照分数升序排序
|
// GetAreasWithScoreAsc 获取所有的编排区域,并按照分数升序排序
|
||||||
func (slf *Editor[ID, AreaInfo]) GetAreasWithScoreAsc(extra ...Item[ID]) []*Area[ID, AreaInfo] {
|
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 {
|
sort.Slice(areas, func(i, j int) bool {
|
||||||
return areas[i].GetScore(extra...) < areas[j].GetScore(extra...)
|
return areas[i].GetScore(extra...) < areas[j].GetScore(extra...)
|
||||||
})
|
})
|
||||||
|
@ -56,7 +55,7 @@ func (slf *Editor[ID, AreaInfo]) GetAreasWithScoreAsc(extra ...Item[ID]) []*Area
|
||||||
|
|
||||||
// GetAreasWithScoreDesc 获取所有的编排区域,并按照分数降序排序
|
// GetAreasWithScoreDesc 获取所有的编排区域,并按照分数降序排序
|
||||||
func (slf *Editor[ID, AreaInfo]) GetAreasWithScoreDesc(extra ...Item[ID]) []*Area[ID, AreaInfo] {
|
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 {
|
sort.Slice(areas, func(i, j int) bool {
|
||||||
return areas[i].GetScore(extra...) > areas[j].GetScore(extra...)
|
return areas[i].GetScore(extra...) > areas[j].GetScore(extra...)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,22 +1,9 @@
|
||||||
package sher
|
package collection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"slices"
|
"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 的快捷方式
|
// CloneSlice 克隆切片,该函数是 slices.Clone 的快捷方式
|
||||||
func CloneSlice[S ~[]V, V any](slice S) S {
|
func CloneSlice[S ~[]V, V any](slice S) S {
|
||||||
return slices.Clone(slice)
|
return slices.Clone(slice)
|
||||||
|
@ -35,11 +22,14 @@ func CloneMap[M ~map[K]V, K comparable, V any](m M) M {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloneSliceN 克隆切片为 n 个切片进行返回
|
// CloneSliceN 克隆 slice 为 n 个切片进行返回
|
||||||
func CloneSliceN[S ~[]V, V any](slice S, n int) []S {
|
func CloneSliceN[S ~[]V, V any](slice S, n int) []S {
|
||||||
if slice == nil {
|
if slice == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if n <= 0 {
|
||||||
|
return []S{}
|
||||||
|
}
|
||||||
|
|
||||||
var result = make([]S, n)
|
var result = make([]S, n)
|
||||||
for i := 0; i < n; i++ {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if n <= 0 {
|
||||||
|
return []M{}
|
||||||
|
}
|
||||||
|
|
||||||
var result = make([]M, n)
|
var result = make([]M, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
result[i] = CloneMap(m)
|
result[i] = CloneMap(m)
|
|
@ -0,0 +1,56 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleCloneSlice() {
|
||||||
|
var slice = []int{1, 2, 3}
|
||||||
|
var result = collection.CloneSlice(slice)
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// [1 2 3]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleCloneMap() {
|
||||||
|
var m = map[int]int{1: 1, 2: 2, 3: 3}
|
||||||
|
var result = collection.CloneMap(m)
|
||||||
|
fmt.Println(len(result))
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleCloneSliceN() {
|
||||||
|
var slice = []int{1, 2, 3}
|
||||||
|
var result = collection.CloneSliceN(slice, 2)
|
||||||
|
fmt.Println(len(result))
|
||||||
|
// Output:
|
||||||
|
// 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleCloneMapN() {
|
||||||
|
var m = map[int]int{1: 1, 2: 2, 3: 3}
|
||||||
|
var result = collection.CloneMapN(m, 2)
|
||||||
|
fmt.Println(len(result))
|
||||||
|
// Output:
|
||||||
|
// 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleCloneSlices() {
|
||||||
|
var slice1 = []int{1, 2, 3}
|
||||||
|
var slice2 = []int{1, 2, 3}
|
||||||
|
var result = collection.CloneSlices(slice1, slice2)
|
||||||
|
fmt.Println(len(result))
|
||||||
|
// Output:
|
||||||
|
// 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleCloneMaps() {
|
||||||
|
var m1 = map[int]int{1: 1, 2: 2, 3: 3}
|
||||||
|
var m2 = map[int]int{1: 1, 2: 2, 3: 3}
|
||||||
|
var result = collection.CloneMaps(m1, m2)
|
||||||
|
fmt.Println(len(result))
|
||||||
|
// Output:
|
||||||
|
// 2
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCloneSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{"TestCloneSlice_NonEmptySlice", []int{1, 2, 3}, []int{1, 2, 3}},
|
||||||
|
{"TestCloneSlice_EmptySlice", []int{}, []int{}},
|
||||||
|
{"TestCloneSlice_NilSlice", nil, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.CloneSlice(c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(actual); i++ {
|
||||||
|
if actual[i] != c.expected[i] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of input is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
expected map[int]int
|
||||||
|
}{
|
||||||
|
{"TestCloneMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{1: 1, 2: 2, 3: 3}},
|
||||||
|
{"TestCloneMap_EmptyMap", map[int]int{}, map[int]int{}},
|
||||||
|
{"TestCloneMap_NilMap", nil, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.CloneMap(c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of map is not equal")
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if v != c.expected[k] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of map is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneSliceN(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
inputN int
|
||||||
|
expected [][]int
|
||||||
|
}{
|
||||||
|
{"TestCloneSliceN_NonEmptySlice", []int{1, 2, 3}, 2, [][]int{{1, 2, 3}, {1, 2, 3}}},
|
||||||
|
{"TestCloneSliceN_EmptySlice", []int{}, 2, [][]int{{}, {}}},
|
||||||
|
{"TestCloneSliceN_NilSlice", nil, 2, nil},
|
||||||
|
{"TestCloneSliceN_NonEmptySlice_ZeroN", []int{1, 2, 3}, 0, [][]int{}},
|
||||||
|
{"TestCloneSliceN_EmptySlice_ZeroN", []int{}, 0, [][]int{}},
|
||||||
|
{"TestCloneSliceN_NilSlice_ZeroN", nil, 0, nil},
|
||||||
|
{"TestCloneSliceN_NonEmptySlice_NegativeN", []int{1, 2, 3}, -1, [][]int{}},
|
||||||
|
{"TestCloneSliceN_EmptySlice_NegativeN", []int{}, -1, [][]int{}},
|
||||||
|
{"TestCloneSliceN_NilSlice_NegativeN", nil, -1, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.CloneSliceN(c.input, c.inputN)
|
||||||
|
if actual == nil {
|
||||||
|
if c.expected != nil {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, c.inputN, actual, "after clone, the expected is nil")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for a, i := range actual {
|
||||||
|
for b, v := range i {
|
||||||
|
if v != c.expected[a][b] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, c.inputN, actual, "after clone, the inputV of input is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneMapN(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
inputN int
|
||||||
|
expected []map[int]int
|
||||||
|
}{
|
||||||
|
{"TestCloneMapN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 2, []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 3: 3}}},
|
||||||
|
{"TestCloneMapN_EmptyMap", map[int]int{}, 2, []map[int]int{{}, {}}},
|
||||||
|
{"TestCloneMapN_NilMap", nil, 2, nil},
|
||||||
|
{"TestCloneMapN_NonEmptyMap_ZeroN", map[int]int{1: 1, 2: 2, 3: 3}, 0, []map[int]int{}},
|
||||||
|
{"TestCloneMapN_EmptyMap_ZeroN", map[int]int{}, 0, []map[int]int{}},
|
||||||
|
{"TestCloneMapN_NilMap_ZeroN", nil, 0, nil},
|
||||||
|
{"TestCloneMapN_NonEmptyMap_NegativeN", map[int]int{1: 1, 2: 2, 3: 3}, -1, []map[int]int{}},
|
||||||
|
{"TestCloneMapN_EmptyMap_NegativeN", map[int]int{}, -1, []map[int]int{}},
|
||||||
|
{"TestCloneMapN_NilMap_NegativeN", nil, -1, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.CloneMapN(c.input, c.inputN)
|
||||||
|
if actual == nil {
|
||||||
|
if c.expected != nil {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, actual, c.inputN, "after clone, the expected is nil")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for a, i := range actual {
|
||||||
|
for b, v := range i {
|
||||||
|
if v != c.expected[a][b] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, actual, c.inputN, "after clone, the inputV of map is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input [][]int
|
||||||
|
expected [][]int
|
||||||
|
}{
|
||||||
|
{"TestCloneSlices_NonEmptySlices", [][]int{{1, 2, 3}, {1, 2, 3}}, [][]int{{1, 2, 3}, {1, 2, 3}}},
|
||||||
|
{"TestCloneSlices_EmptySlices", [][]int{{}, {}}, [][]int{{}, {}}},
|
||||||
|
{"TestCloneSlices_NilSlices", nil, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.CloneSlices(c.input...)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
for a, i := range actual {
|
||||||
|
for b, v := range i {
|
||||||
|
if v != c.expected[a][b] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of input is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneMaps(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []map[int]int
|
||||||
|
expected []map[int]int
|
||||||
|
}{
|
||||||
|
{"TestCloneMaps_NonEmptyMaps", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 3: 3}}, []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 3: 3}}},
|
||||||
|
{"TestCloneMaps_EmptyMaps", []map[int]int{{}, {}}, []map[int]int{{}, {}}},
|
||||||
|
{"TestCloneMaps_NilMaps", nil, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.CloneMaps(c.input...)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of maps is not equal")
|
||||||
|
}
|
||||||
|
for a, i := range actual {
|
||||||
|
for b, v := range i {
|
||||||
|
if v != c.expected[a][b] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of maps is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,331 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
type ComparisonHandler[V any] func(source, target V) bool
|
||||||
|
|
||||||
|
// InSlice 检查 v 是否被包含在 slice 中,当 handler 返回 true 时,表示 v 和 slice 中的某个元素相匹配
|
||||||
|
func InSlice[S ~[]V, V any](slice S, v V, handler ComparisonHandler[V]) bool {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, value := range slice {
|
||||||
|
if handler(v, value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// InComparableSlice 检查 v 是否被包含在 slice 中
|
||||||
|
func InComparableSlice[S ~[]V, V comparable](slice S, v V) bool {
|
||||||
|
if slice == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, value := range slice {
|
||||||
|
if value == v {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllInSlice 检查 values 中的所有元素是否均被包含在 slice 中,当 handler 返回 true 时,表示 values 中的某个元素和 slice 中的某个元素相匹配
|
||||||
|
// - 在所有 values 中的元素都被包含在 slice 中时,返回 true
|
||||||
|
// - 当 values 长度为 0 或为 nil 时,将返回 true
|
||||||
|
func AllInSlice[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
if !InSlice(slice, value, handler) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllInComparableSlice 检查 values 中的所有元素是否均被包含在 slice 中
|
||||||
|
// - 在所有 values 中的元素都被包含在 slice 中时,返回 true
|
||||||
|
// - 当 values 长度为 0 或为 nil 时,将返回 true
|
||||||
|
func AllInComparableSlice[S ~[]V, V comparable](slice S, values []V) bool {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
if !InComparableSlice(slice, value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyInSlice 检查 values 中的任意一个元素是否被包含在 slice 中,当 handler 返回 true 时,表示 value 中的某个元素和 slice 中的某个元素相匹配
|
||||||
|
// - 当 values 中的任意一个元素被包含在 slice 中时,返回 true
|
||||||
|
func AnyInSlice[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
if InSlice(slice, value, handler) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyInComparableSlice 检查 values 中的任意一个元素是否被包含在 slice 中
|
||||||
|
// - 当 values 中的任意一个元素被包含在 slice 中时,返回 true
|
||||||
|
func AnyInComparableSlice[S ~[]V, V comparable](slice S, values []V) bool {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
if InComparableSlice(slice, value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// InSlices 通过将多个切片合并后检查 v 是否被包含在 slices 中,当 handler 返回 true 时,表示 v 和 slices 中的某个元素相匹配
|
||||||
|
// - 当传入的 v 被包含在 slices 的任一成员中时,返回 true
|
||||||
|
func InSlices[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
||||||
|
return InSlice(MergeSlices(slices...), v, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InComparableSlices 通过将多个切片合并后检查 v 是否被包含在 slices 中
|
||||||
|
// - 当传入的 v 被包含在 slices 的任一成员中时,返回 true
|
||||||
|
func InComparableSlices[S ~[]V, V comparable](slices []S, v V) bool {
|
||||||
|
return InComparableSlice(MergeSlices(slices...), v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllInSlices 通过将多个切片合并后检查 values 中的所有元素是否被包含在 slices 中,当 handler 返回 true 时,表示 values 中的某个元素和 slices 中的某个元素相匹配
|
||||||
|
// - 当 values 中的所有元素都被包含在 slices 的任一成员中时,返回 true
|
||||||
|
func AllInSlices[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
||||||
|
return AllInSlice(MergeSlices(slices...), values, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllInComparableSlices 通过将多个切片合并后检查 values 中的所有元素是否被包含在 slices 中
|
||||||
|
// - 当 values 中的所有元素都被包含在 slices 的任一成员中时,返回 true
|
||||||
|
func AllInComparableSlices[S ~[]V, V comparable](slices []S, values []V) bool {
|
||||||
|
return AllInComparableSlice(MergeSlices(slices...), values)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyInSlices 通过将多个切片合并后检查 values 中的任意一个元素是否被包含在 slices 中,当 handler 返回 true 时,表示 values 中的某个元素和 slices 中的某个元素相匹配
|
||||||
|
// - 当 values 中的任意一个元素被包含在 slices 的任一成员中时,返回 true
|
||||||
|
func AnyInSlices[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
||||||
|
return AnyInSlice(MergeSlices(slices...), values, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyInComparableSlices 通过将多个切片合并后检查 values 中的任意一个元素是否被包含在 slices 中
|
||||||
|
// - 当 values 中的任意一个元素被包含在 slices 的任一成员中时,返回 true
|
||||||
|
func AnyInComparableSlices[S ~[]V, V comparable](slices []S, values []V) bool {
|
||||||
|
return AnyInComparableSlice(MergeSlices(slices...), values)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InAllSlices 检查 v 是否被包含在 slices 的每一项元素中,当 handler 返回 true 时,表示 v 和 slices 中的某个元素相匹配
|
||||||
|
// - 当 v 被包含在 slices 的每一项元素中时,返回 true
|
||||||
|
func InAllSlices[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
||||||
|
if len(slices) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, slice := range slices {
|
||||||
|
if !InSlice(slice, v, handler) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// InAllComparableSlices 检查 v 是否被包含在 slices 的每一项元素中
|
||||||
|
// - 当 v 被包含在 slices 的每一项元素中时,返回 true
|
||||||
|
func InAllComparableSlices[S ~[]V, V comparable](slices []S, v V) bool {
|
||||||
|
if len(slices) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, slice := range slices {
|
||||||
|
if !InComparableSlice(slice, v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyInAllSlices 检查 slices 中的每一个元素是否均包含至少任意一个 values 中的元素,当 handler 返回 true 时,表示 value 中的某个元素和 slices 中的某个元素相匹配
|
||||||
|
// - 当 slices 中的每一个元素均包含至少任意一个 values 中的元素时,返回 true
|
||||||
|
func AnyInAllSlices[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
||||||
|
if len(slices) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, slice := range slices {
|
||||||
|
if !AnyInSlice(slice, values, handler) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyInAllComparableSlices 检查 slices 中的每一个元素是否均包含至少任意一个 values 中的元素
|
||||||
|
// - 当 slices 中的每一个元素均包含至少任意一个 values 中的元素时,返回 true
|
||||||
|
func AnyInAllComparableSlices[S ~[]V, V comparable](slices []S, values []V) bool {
|
||||||
|
if len(slices) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, slice := range slices {
|
||||||
|
if !AnyInComparableSlice(slice, values) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyInMap 检查 m 中是否包含特定 key
|
||||||
|
func KeyInMap[M ~map[K]V, K comparable, V any](m M, key K) bool {
|
||||||
|
_, ok := m[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueInMap 检查 m 中是否包含特定 value,当 handler 返回 true 时,表示 value 和 m 中的某个元素相匹配
|
||||||
|
func ValueInMap[M ~map[K]V, K comparable, V any](m M, value V, handler ComparisonHandler[V]) bool {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, v := range m {
|
||||||
|
if handler(value, v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllKeyInMap 检查 m 中是否包含 keys 中所有的元素
|
||||||
|
func AllKeyInMap[M ~map[K]V, K comparable, V any](m M, keys ...K) bool {
|
||||||
|
if len(m) < len(keys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
if !KeyInMap(m, key) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllValueInMap 检查 m 中是否包含 values 中所有的元素,当 handler 返回 true 时,表示 values 中的某个元素和 m 中的某个元素相匹配
|
||||||
|
func AllValueInMap[M ~map[K]V, K comparable, V any](m M, values []V, handler ComparisonHandler[V]) bool {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
if !ValueInMap(m, value, handler) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyKeyInMap 检查 m 中是否包含 keys 中任意一个元素
|
||||||
|
func AnyKeyInMap[M ~map[K]V, K comparable, V any](m M, keys ...K) bool {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
if KeyInMap(m, key) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyValueInMap 检查 m 中是否包含 values 中任意一个元素,当 handler 返回 true 时,表示 values 中的某个元素和 m 中的某个元素相匹配
|
||||||
|
func AnyValueInMap[M ~map[K]V, K comparable, V any](m M, values []V, handler ComparisonHandler[V]) bool {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
if ValueInMap(m, value, handler) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllKeyInMaps 检查 maps 中的每一个元素是否均包含 keys 中所有的元素
|
||||||
|
func AllKeyInMaps[M ~map[K]V, K comparable, V any](maps []M, keys ...K) bool {
|
||||||
|
if len(maps) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, m := range maps {
|
||||||
|
if !AllKeyInMap(m, keys...) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllValueInMaps 检查 maps 中的每一个元素是否均包含 value 中所有的元素,当 handler 返回 true 时,表示 value 中的某个元素和 maps 中的某个元素相匹配
|
||||||
|
func AllValueInMaps[M ~map[K]V, K comparable, V any](maps []M, values []V, handler ComparisonHandler[V]) bool {
|
||||||
|
if len(maps) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, m := range maps {
|
||||||
|
if !AllValueInMap(m, values, handler) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyKeyInMaps 检查 keys 中的任意一个元素是否被包含在 maps 中的任意一个元素中
|
||||||
|
// - 当 keys 中的任意一个元素被包含在 maps 中的任意一个元素中时,返回 true
|
||||||
|
func AnyKeyInMaps[M ~map[K]V, K comparable, V any](maps []M, keys ...K) bool {
|
||||||
|
if len(maps) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, m := range maps {
|
||||||
|
if AnyKeyInMap(m, keys...) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyValueInMaps 检查 maps 中的任意一个元素是否包含 value 中的任意一个元素,当 handler 返回 true 时,表示 value 中的某个元素和 maps 中的某个元素相匹配
|
||||||
|
// - 当 maps 中的任意一个元素包含 value 中的任意一个元素时,返回 true
|
||||||
|
func AnyValueInMaps[M ~map[K]V, K comparable, V any](maps []M, values []V, handler ComparisonHandler[V]) bool {
|
||||||
|
if len(maps) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, m := range maps {
|
||||||
|
if !AnyValueInMap(m, values, handler) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyInAllMaps 检查 key 是否被包含在 maps 的每一个元素中
|
||||||
|
func KeyInAllMaps[M ~map[K]V, K comparable, V any](maps []M, key K) bool {
|
||||||
|
if len(maps) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, m := range maps {
|
||||||
|
if !KeyInMap(m, key) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyKeyInAllMaps 检查 maps 中的每一个元素是否均包含 keys 中任意一个元素
|
||||||
|
// - 当 maps 中的每一个元素均包含 keys 中任意一个元素时,返回 true
|
||||||
|
func AnyKeyInAllMaps[M ~map[K]V, K comparable, V any](maps []M, keys []K) bool {
|
||||||
|
if len(maps) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, m := range maps {
|
||||||
|
if !AnyKeyInMap(m, keys...) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,228 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleInSlice() {
|
||||||
|
result := collection.InSlice([]int{1, 2, 3}, 2, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleInComparableSlice() {
|
||||||
|
result := collection.InComparableSlice([]int{1, 2, 3}, 2)
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAllInSlice() {
|
||||||
|
result := collection.AllInSlice([]int{1, 2, 3}, []int{1, 2}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAllInComparableSlice() {
|
||||||
|
result := collection.AllInComparableSlice([]int{1, 2, 3}, []int{1, 2})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyInSlice() {
|
||||||
|
result := collection.AnyInSlice([]int{1, 2, 3}, []int{1, 2}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyInComparableSlice() {
|
||||||
|
result := collection.AnyInComparableSlice([]int{1, 2, 3}, []int{1, 2})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleInSlices() {
|
||||||
|
result := collection.InSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleInComparableSlices() {
|
||||||
|
result := collection.InComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2)
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAllInSlices() {
|
||||||
|
result := collection.AllInSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAllInComparableSlices() {
|
||||||
|
result := collection.AllInComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyInSlices() {
|
||||||
|
result := collection.AnyInSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyInComparableSlices() {
|
||||||
|
result := collection.AnyInComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleInAllSlices() {
|
||||||
|
result := collection.InAllSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleInAllComparableSlices() {
|
||||||
|
result := collection.InAllComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2)
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyInAllSlices() {
|
||||||
|
result := collection.AnyInAllSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyInAllComparableSlices() {
|
||||||
|
result := collection.AnyInAllComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleKeyInMap() {
|
||||||
|
result := collection.KeyInMap(map[string]int{"a": 1, "b": 2}, "a")
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleValueInMap() {
|
||||||
|
result := collection.ValueInMap(map[string]int{"a": 1, "b": 2}, 2, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAllKeyInMap() {
|
||||||
|
result := collection.AllKeyInMap(map[string]int{"a": 1, "b": 2}, "a", "b")
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAllValueInMap() {
|
||||||
|
result := collection.AllValueInMap(map[string]int{"a": 1, "b": 2}, []int{1}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyKeyInMap() {
|
||||||
|
result := collection.AnyKeyInMap(map[string]int{"a": 1, "b": 2}, "a", "b")
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyValueInMap() {
|
||||||
|
result := collection.AnyValueInMap(map[string]int{"a": 1, "b": 2}, []int{1}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAllKeyInMaps() {
|
||||||
|
result := collection.AllKeyInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, "a", "b")
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAllValueInMaps() {
|
||||||
|
result := collection.AllValueInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, []int{1}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyKeyInMaps() {
|
||||||
|
result := collection.AnyKeyInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, "a", "b")
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyValueInMaps() {
|
||||||
|
result := collection.AnyValueInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, []int{1}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleKeyInAllMaps() {
|
||||||
|
result := collection.KeyInAllMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, "a")
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnyKeyInAllMaps() {
|
||||||
|
result := collection.AnyKeyInAllMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, []string{"a"})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
|
@ -0,0 +1,709 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var intComparisonHandler = func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
inputV int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestInSlice_NonEmptySliceIn", []int{1, 2, 3}, 1, true},
|
||||||
|
{"TestInSlice_NonEmptySliceNotIn", []int{1, 2, 3}, 4, false},
|
||||||
|
{"TestInSlice_EmptySlice", []int{}, 1, false},
|
||||||
|
{"TestInSlice_NilSlice", nil, 1, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.InSlice(c.input, c.inputV, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInComparableSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
inputV int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestInComparableSlice_NonEmptySliceIn", []int{1, 2, 3}, 1, true},
|
||||||
|
{"TestInComparableSlice_NonEmptySliceNotIn", []int{1, 2, 3}, 4, false},
|
||||||
|
{"TestInComparableSlice_EmptySlice", []int{}, 1, false},
|
||||||
|
{"TestInComparableSlice_NilSlice", nil, 1, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.InComparableSlice(c.input, c.inputV)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllInSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
inputV []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAllInSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true},
|
||||||
|
{"TestAllInSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, false},
|
||||||
|
{"TestAllInSlice_EmptySlice", []int{}, []int{1, 2}, false},
|
||||||
|
{"TestAllInSlice_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAllInSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, true},
|
||||||
|
{"TestAllInSlice_NilValueSlice", []int{1, 2, 3}, nil, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AllInSlice(c.input, c.inputV, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllInComparableSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
inputV []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAllInComparableSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true},
|
||||||
|
{"TestAllInComparableSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, false},
|
||||||
|
{"TestAllInComparableSlice_EmptySlice", []int{}, []int{1, 2}, false},
|
||||||
|
{"TestAllInComparableSlice_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAllInComparableSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, true},
|
||||||
|
{"TestAllInComparableSlice_NilValueSlice", []int{1, 2, 3}, nil, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AllInComparableSlice(c.input, c.inputV)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyInSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
inputV []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyInSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true},
|
||||||
|
{"TestAnyInSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, true},
|
||||||
|
{"TestAnyInSlice_EmptySlice", []int{}, []int{1, 2}, false},
|
||||||
|
{"TestAnyInSlice_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAnyInSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, false},
|
||||||
|
{"TestAnyInSlice_NilValueSlice", []int{1, 2, 3}, nil, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyInSlice(c.input, c.inputV, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyInComparableSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
inputV []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyInComparableSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true},
|
||||||
|
{"TestAnyInComparableSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, true},
|
||||||
|
{"TestAnyInComparableSlice_EmptySlice", []int{}, []int{1, 2}, false},
|
||||||
|
{"TestAnyInComparableSlice_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAnyInComparableSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, false},
|
||||||
|
{"TestAnyInComparableSlice_NilValueSlice", []int{1, 2, 3}, nil, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyInComparableSlice(c.input, c.inputV)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input [][]int
|
||||||
|
inputV int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestInSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, 1, true},
|
||||||
|
{"TestInSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false},
|
||||||
|
{"TestInSlices_EmptySlice", [][]int{{}, {}}, 1, false},
|
||||||
|
{"TestInSlices_NilSlice", nil, 1, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.InSlices(c.input, c.inputV, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInComparableSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input [][]int
|
||||||
|
inputV int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestInComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, 1, true},
|
||||||
|
{"TestInComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false},
|
||||||
|
{"TestInComparableSlices_EmptySlice", [][]int{{}, {}}, 1, false},
|
||||||
|
{"TestInComparableSlices_NilSlice", nil, 1, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.InComparableSlices(c.input, c.inputV)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllInSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input [][]int
|
||||||
|
inputValues []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAllInSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true},
|
||||||
|
{"TestAllInSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false},
|
||||||
|
{"TestAllInSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAllInSlices_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAllInSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, true},
|
||||||
|
{"TestAllInSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AllInSlices(c.input, c.inputValues, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllInComparableSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input [][]int
|
||||||
|
inputValues []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAllInComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true},
|
||||||
|
{"TestAllInComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false},
|
||||||
|
{"TestAllInComparableSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAllInComparableSlices_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAllInComparableSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, true},
|
||||||
|
{"TestAllInComparableSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AllInComparableSlices(c.input, c.inputValues)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyInSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
slices [][]int
|
||||||
|
values []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyInSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true},
|
||||||
|
{"TestAnyInSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, true},
|
||||||
|
{"TestAnyInSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyInSlices_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAnyInSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false},
|
||||||
|
{"TestAnyInSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyInSlices(c.slices, c.values, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyInComparableSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
slices [][]int
|
||||||
|
values []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyInComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true},
|
||||||
|
{"TestAnyInComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, true},
|
||||||
|
{"TestAnyInComparableSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyInComparableSlices_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAnyInComparableSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false},
|
||||||
|
{"TestAnyInComparableSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyInComparableSlices(c.slices, c.values)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInAllSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
slices [][]int
|
||||||
|
value int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestInAllSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, 1, true},
|
||||||
|
{"TestInAllSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false},
|
||||||
|
{"TestInAllSlices_EmptySlice", [][]int{{}, {}}, 1, false},
|
||||||
|
{"TestInAllSlices_NilSlice", nil, 1, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.InAllSlices(c.slices, c.value, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInAllComparableSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
slices [][]int
|
||||||
|
value int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestInAllComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, 1, true},
|
||||||
|
{"TestInAllComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false},
|
||||||
|
{"TestInAllComparableSlices_EmptySlice", [][]int{{}, {}}, 1, false},
|
||||||
|
{"TestInAllComparableSlices_NilSlice", nil, 1, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.InAllComparableSlices(c.slices, c.value)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyInAllSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
slices [][]int
|
||||||
|
values []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyInAllSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, []int{1, 2}, true},
|
||||||
|
{"TestAnyInAllSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false},
|
||||||
|
{"TestAnyInAllSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyInAllSlices_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAnyInAllSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false},
|
||||||
|
{"TestAnyInAllSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyInAllSlices(c.slices, c.values, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyInAllComparableSlices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
slices [][]int
|
||||||
|
values []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyInAllComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, []int{1, 2}, true},
|
||||||
|
{"TestAnyInAllComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false},
|
||||||
|
{"TestAnyInAllComparableSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyInAllComparableSlices_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAnyInAllComparableSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false},
|
||||||
|
{"TestAnyInAllComparableSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyInAllComparableSlices(c.slices, c.values)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyInMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
m map[int]int
|
||||||
|
key int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestKeyInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, 1, true},
|
||||||
|
{"TestKeyInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, 3, false},
|
||||||
|
{"TestKeyInMap_EmptySlice", map[int]int{}, 1, false},
|
||||||
|
{"TestKeyInMap_NilSlice", nil, 1, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.KeyInMap(c.m, c.key)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueInMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
m map[int]int
|
||||||
|
value int
|
||||||
|
handler collection.ComparisonHandler[int]
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestValueInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, 1, intComparisonHandler, true},
|
||||||
|
{"TestValueInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, 3, intComparisonHandler, false},
|
||||||
|
{"TestValueInMap_EmptySlice", map[int]int{}, 1, intComparisonHandler, false},
|
||||||
|
{"TestValueInMap_NilSlice", nil, 1, intComparisonHandler, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.ValueInMap(c.m, c.value, c.handler)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllKeyInMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
m map[int]int
|
||||||
|
keys []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAllKeyInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true},
|
||||||
|
{"TestAllKeyInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, false},
|
||||||
|
{"TestAllKeyInMap_EmptySlice", map[int]int{}, []int{1, 2}, false},
|
||||||
|
{"TestAllKeyInMap_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AllKeyInMap(c.m, c.keys...)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllValueInMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
m map[int]int
|
||||||
|
values []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAllValueInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true},
|
||||||
|
{"TestAllValueInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, false},
|
||||||
|
{"TestAllValueInMap_EmptySlice", map[int]int{}, []int{1, 2}, false},
|
||||||
|
{"TestAllValueInMap_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AllValueInMap(c.m, c.values, intComparisonHandler)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyKeyInMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
m map[int]int
|
||||||
|
keys []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyKeyInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true},
|
||||||
|
{"TestAnyKeyInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, true},
|
||||||
|
{"TestAnyKeyInMap_EmptySlice", map[int]int{}, []int{1, 2}, false},
|
||||||
|
{"TestAnyKeyInMap_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyKeyInMap(c.m, c.keys...)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyValueInMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
m map[int]int
|
||||||
|
values []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyValueInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true},
|
||||||
|
{"TestAnyValueInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, true},
|
||||||
|
{"TestAnyValueInMap_EmptySlice", map[int]int{}, []int{1, 2}, false},
|
||||||
|
{"TestAnyValueInMap_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyValueInMap(c.m, c.values, intComparisonHandler)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllKeyInMaps(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
maps []map[int]int
|
||||||
|
keys []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAllKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false},
|
||||||
|
{"TestAllKeyInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, false},
|
||||||
|
{"TestAllKeyInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAllKeyInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAllKeyInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false},
|
||||||
|
{"TestAllKeyInMaps_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAllKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AllKeyInMaps(c.maps, c.keys...)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllValueInMaps(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
maps []map[int]int
|
||||||
|
values []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAllValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false},
|
||||||
|
{"TestAllValueInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, false},
|
||||||
|
{"TestAllValueInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAllValueInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAllValueInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false},
|
||||||
|
{"TestAllValueInMaps_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAllValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AllValueInMaps(c.maps, c.values, intComparisonHandler)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyKeyInMaps(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
maps []map[int]int
|
||||||
|
keys []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, true},
|
||||||
|
{"TestAnyKeyInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, true},
|
||||||
|
{"TestAnyKeyInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, true},
|
||||||
|
{"TestAnyKeyInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyKeyInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false},
|
||||||
|
{"TestAnyKeyInMaps_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAnyKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyKeyInMaps(c.maps, c.keys...)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyValueInMaps(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
maps []map[int]int
|
||||||
|
values []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyValueInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, true},
|
||||||
|
{"TestAnyValueInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyValueInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyValueInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false},
|
||||||
|
{"TestAnyValueInMaps_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAnyValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyValueInMaps(c.maps, c.values, intComparisonHandler)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyInAllMaps(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
maps []map[int]int
|
||||||
|
key int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, 1, false},
|
||||||
|
{"TestKeyInAllMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, 3, false},
|
||||||
|
{"TestKeyInAllMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, 1, false},
|
||||||
|
{"TestKeyInAllMaps_NilSlice", []map[int]int{{}, {}}, 1, false},
|
||||||
|
{"TestKeyInAllMaps_EmptySlice", []map[int]int{}, 1, false},
|
||||||
|
{"TestKeyInAllMaps_NilSlice", nil, 1, false},
|
||||||
|
{"TestKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, 1, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.KeyInAllMaps(c.maps, c.key)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnyKeyInAllMaps(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
maps []map[int]int
|
||||||
|
keys []int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"TestAnyKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyKeyInAllMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, true},
|
||||||
|
{"TestAnyKeyInAllMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyKeyInAllMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false},
|
||||||
|
{"TestAnyKeyInAllMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false},
|
||||||
|
{"TestAnyKeyInAllMaps_NilSlice", nil, []int{1, 2}, false},
|
||||||
|
{"TestAnyKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var actual = collection.AnyKeyInAllMaps(c.maps, c.keys)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
package sher
|
package collection
|
||||||
|
|
||||||
// ConvertSliceToAny 将切片转换为任意类型的切片
|
// ConvertSliceToAny 将切片转换为任意类型的切片
|
||||||
func ConvertSliceToAny[S ~[]V, V any](s S) []any {
|
func ConvertSliceToAny[S ~[]V, V any](s S) []any {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var r = make([]any, len(s))
|
var r = make([]any, len(s))
|
||||||
for i, v := range s {
|
for i, v := range s {
|
||||||
r[i] = v
|
r[i] = v
|
||||||
|
@ -11,6 +14,9 @@ func ConvertSliceToAny[S ~[]V, V any](s S) []any {
|
||||||
|
|
||||||
// ConvertSliceToIndexMap 将切片转换为索引为键的映射
|
// ConvertSliceToIndexMap 将切片转换为索引为键的映射
|
||||||
func ConvertSliceToIndexMap[S ~[]V, V any](s S) map[int]V {
|
func ConvertSliceToIndexMap[S ~[]V, V any](s S) map[int]V {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var r = make(map[int]V, len(s))
|
var r = make(map[int]V, len(s))
|
||||||
for i, v := range s {
|
for i, v := range s {
|
||||||
r[i] = v
|
r[i] = v
|
||||||
|
@ -20,6 +26,9 @@ func ConvertSliceToIndexMap[S ~[]V, V any](s S) map[int]V {
|
||||||
|
|
||||||
// ConvertSliceToMap 将切片转换为值为键的映射
|
// ConvertSliceToMap 将切片转换为值为键的映射
|
||||||
func ConvertSliceToMap[S ~[]V, V comparable](s S) map[V]struct{} {
|
func ConvertSliceToMap[S ~[]V, V comparable](s S) map[V]struct{} {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var r = make(map[V]struct{}, len(s))
|
var r = make(map[V]struct{}, len(s))
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
r[v] = struct{}{}
|
r[v] = struct{}{}
|
||||||
|
@ -29,6 +38,9 @@ func ConvertSliceToMap[S ~[]V, V comparable](s S) map[V]struct{} {
|
||||||
|
|
||||||
// ConvertSliceToBoolMap 将切片转换为值为键的映射
|
// ConvertSliceToBoolMap 将切片转换为值为键的映射
|
||||||
func ConvertSliceToBoolMap[S ~[]V, V comparable](s S) map[V]bool {
|
func ConvertSliceToBoolMap[S ~[]V, V comparable](s S) map[V]bool {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var r = make(map[V]bool, len(s))
|
var r = make(map[V]bool, len(s))
|
||||||
for _, v := range s {
|
for _, v := range s {
|
||||||
r[v] = true
|
r[v] = true
|
||||||
|
@ -38,6 +50,9 @@ func ConvertSliceToBoolMap[S ~[]V, V comparable](s S) map[V]bool {
|
||||||
|
|
||||||
// ConvertMapKeysToSlice 将映射的键转换为切片
|
// ConvertMapKeysToSlice 将映射的键转换为切片
|
||||||
func ConvertMapKeysToSlice[M ~map[K]V, K comparable, V any](m M) []K {
|
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))
|
var r = make([]K, 0, len(m))
|
||||||
for k := range m {
|
for k := range m {
|
||||||
r = append(r, k)
|
r = append(r, k)
|
||||||
|
@ -47,9 +62,48 @@ func ConvertMapKeysToSlice[M ~map[K]V, K comparable, V any](m M) []K {
|
||||||
|
|
||||||
// ConvertMapValuesToSlice 将映射的值转换为切片
|
// ConvertMapValuesToSlice 将映射的值转换为切片
|
||||||
func ConvertMapValuesToSlice[M ~map[K]V, K comparable, V any](m M) []V {
|
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))
|
var r = make([]V, 0, len(m))
|
||||||
for _, v := range m {
|
for _, v := range m {
|
||||||
r = append(r, v)
|
r = append(r, v)
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InvertMap 将映射的键和值互换
|
||||||
|
func InvertMap[M ~map[K]V, N ~map[V]K, K, V comparable](m M) N {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var r = make(N, len(m))
|
||||||
|
for k, v := range m {
|
||||||
|
r[v] = k
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertMapValuesToBool 将映射的值转换为布尔值
|
||||||
|
func ConvertMapValuesToBool[M ~map[K]V, N ~map[K]bool, K comparable, V any](m M) N {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var r = make(N, len(m))
|
||||||
|
for k := range m {
|
||||||
|
r[k] = true
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReverseSlice 将切片反转
|
||||||
|
func ReverseSlice[S ~[]V, V any](s *S) {
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var length = len(*s)
|
||||||
|
for i := 0; i < length/2; i++ {
|
||||||
|
(*s)[i], (*s)[length-i-1] = (*s)[length-i-1], (*s)[i]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package collection_test
|
|
@ -0,0 +1,264 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConvertSliceToAny(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected []interface{}
|
||||||
|
}{
|
||||||
|
{name: "TestConvertSliceToAny_NonEmpty", input: []int{1, 2, 3}, expected: []any{1, 2, 3}},
|
||||||
|
{name: "TestConvertSliceToAny_Empty", input: []int{}, expected: []any{}},
|
||||||
|
{name: "TestConvertSliceToAny_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.ConvertSliceToAny(c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(actual); i++ {
|
||||||
|
av, ev := actual[i], c.expected[i]
|
||||||
|
if reflect.TypeOf(av).Kind() != reflect.TypeOf(ev).Kind() {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
if av != ev {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertSliceToIndexMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected map[int]int
|
||||||
|
}{
|
||||||
|
{name: "TestConvertSliceToIndexMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]int{0: 1, 1: 2, 2: 3}},
|
||||||
|
{name: "TestConvertSliceToIndexMap_Empty", input: []int{}, expected: map[int]int{}},
|
||||||
|
{name: "TestConvertSliceToIndexMap_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.ConvertSliceToIndexMap(c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if c.expected[k] != v {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertSliceToMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected map[int]struct{}
|
||||||
|
}{
|
||||||
|
{name: "TestConvertSliceToMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]struct{}{1: {}, 2: {}, 3: {}}},
|
||||||
|
{name: "TestConvertSliceToMap_Empty", input: []int{}, expected: map[int]struct{}{}},
|
||||||
|
{name: "TestConvertSliceToMap_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.ConvertSliceToMap(c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if _, ok := c.expected[k]; !ok {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
if v != struct{}{} {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertSliceToBoolMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected map[int]bool
|
||||||
|
}{
|
||||||
|
{name: "TestConvertSliceToBoolMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]bool{1: true, 2: true, 3: true}},
|
||||||
|
{name: "TestConvertSliceToBoolMap_Empty", input: []int{}, expected: map[int]bool{}},
|
||||||
|
{name: "TestConvertSliceToBoolMap_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.ConvertSliceToBoolMap(c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if c.expected[k] != v {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertMapKeysToSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{name: "TestConvertMapKeysToSlice_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, expected: []int{1, 2, 3}},
|
||||||
|
{name: "TestConvertMapKeysToSlice_Empty", input: map[int]int{}, expected: []int{}},
|
||||||
|
{name: "TestConvertMapKeysToSlice_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.ConvertMapKeysToSlice(c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
var matchCount = 0
|
||||||
|
for _, av := range actual {
|
||||||
|
for _, ev := range c.expected {
|
||||||
|
if av == ev {
|
||||||
|
matchCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matchCount != len(actual) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertMapValuesToSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{name: "TestConvertMapValuesToSlice_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, expected: []int{1, 2, 3}},
|
||||||
|
{name: "TestConvertMapValuesToSlice_Empty", input: map[int]int{}, expected: []int{}},
|
||||||
|
{name: "TestConvertMapValuesToSlice_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.ConvertMapValuesToSlice(c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
var matchCount = 0
|
||||||
|
for _, av := range actual {
|
||||||
|
for _, ev := range c.expected {
|
||||||
|
if av == ev {
|
||||||
|
matchCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matchCount != len(actual) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvertMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]string
|
||||||
|
expected map[string]int
|
||||||
|
}{
|
||||||
|
{name: "TestInvertMap_NonEmpty", input: map[int]string{1: "1", 2: "2", 3: "3"}, expected: map[string]int{"1": 1, "2": 2, "3": 3}},
|
||||||
|
{name: "TestInvertMap_Empty", input: map[int]string{}, expected: map[string]int{}},
|
||||||
|
{name: "TestInvertMap_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.InvertMap[map[int]string, map[string]int](c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if c.expected[k] != v {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertMapValuesToBool(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
expected map[int]bool
|
||||||
|
}{
|
||||||
|
{name: "TestConvertMapValuesToBool_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, expected: map[int]bool{1: true, 2: true, 3: true}},
|
||||||
|
{name: "TestConvertMapValuesToBool_Empty", input: map[int]int{}, expected: map[int]bool{}},
|
||||||
|
{name: "TestConvertMapValuesToBool_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.ConvertMapValuesToBool[map[int]int, map[int]bool](c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if c.expected[k] != v {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReverseSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{name: "TestReverseSlice_NonEmpty", input: []int{1, 2, 3}, expected: []int{3, 2, 1}},
|
||||||
|
{name: "TestReverseSlice_Empty", input: []int{}, expected: []int{}},
|
||||||
|
{name: "TestReverseSlice_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
collection.ReverseSlice(&c.input)
|
||||||
|
if len(c.input) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(c.input); i++ {
|
||||||
|
av, ev := c.input[i], c.expected[i]
|
||||||
|
if reflect.TypeOf(av).Kind() != reflect.TypeOf(ev).Kind() {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||||
|
}
|
||||||
|
if av != ev {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Package collection 用于对 input 和 map 操作的工具函数
|
||||||
|
package collection
|
|
@ -0,0 +1,82 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
// ClearSlice 清空切片
|
||||||
|
func ClearSlice[S ~[]V, V any](slice *S) {
|
||||||
|
if slice == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*slice = (*slice)[0:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearMap 清空 map
|
||||||
|
func ClearMap[M ~map[K]V, K comparable, V any](m M) {
|
||||||
|
for k := range m {
|
||||||
|
delete(m, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropSliceByIndices 删除切片中特定索引的元素
|
||||||
|
func DropSliceByIndices[S ~[]V, V any](slice *S, indices ...int) {
|
||||||
|
if slice == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(indices) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
excludeMap := make(map[int]bool)
|
||||||
|
for _, ex := range indices {
|
||||||
|
excludeMap[ex] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
validElements := (*slice)[:0]
|
||||||
|
for i, v := range *slice {
|
||||||
|
if !excludeMap[i] {
|
||||||
|
validElements = append(validElements, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*slice = validElements
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropSliceByCondition 删除切片中符合条件的元素
|
||||||
|
// - condition 的返回值为 true 时,将会删除该元素
|
||||||
|
func DropSliceByCondition[S ~[]V, V any](slice *S, condition func(v V) bool) {
|
||||||
|
if slice == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if condition == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
validElements := (*slice)[:0]
|
||||||
|
for _, v := range *slice {
|
||||||
|
if !condition(v) {
|
||||||
|
validElements = append(validElements, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*slice = validElements
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropSliceOverlappingElements 删除切片中与另一个切片重叠的元素
|
||||||
|
func DropSliceOverlappingElements[S ~[]V, V any](slice *S, anotherSlice []V, comparisonHandler ComparisonHandler[V]) {
|
||||||
|
if slice == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if anotherSlice == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if comparisonHandler == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
validElements := (*slice)[:0]
|
||||||
|
for _, v := range *slice {
|
||||||
|
if !InSlice(anotherSlice, v, comparisonHandler) {
|
||||||
|
validElements = append(validElements, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*slice = validElements
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func ExampleClearSlice() {
|
||||||
|
slice := []int{1, 2, 3, 4, 5}
|
||||||
|
ClearSlice(&slice)
|
||||||
|
fmt.Println(slice)
|
||||||
|
// Output:
|
||||||
|
// []
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleClearMap() {
|
||||||
|
m := map[int]int{1: 1, 2: 2, 3: 3}
|
||||||
|
ClearMap(m)
|
||||||
|
fmt.Println(m)
|
||||||
|
// Output:
|
||||||
|
// map[]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDropSliceByIndices() {
|
||||||
|
slice := []int{1, 2, 3, 4, 5}
|
||||||
|
DropSliceByIndices(&slice, 1, 3)
|
||||||
|
fmt.Println(slice)
|
||||||
|
// Output:
|
||||||
|
// [1 3 5]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDropSliceByCondition() {
|
||||||
|
slice := []int{1, 2, 3, 4, 5}
|
||||||
|
DropSliceByCondition(&slice, func(v int) bool {
|
||||||
|
return v%2 == 0
|
||||||
|
})
|
||||||
|
fmt.Println(slice)
|
||||||
|
// Output:
|
||||||
|
// [1 3 5]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDropSliceOverlappingElements() {
|
||||||
|
slice := []int{1, 2, 3, 4, 5}
|
||||||
|
DropSliceOverlappingElements(&slice, []int{1, 3, 5}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(slice)
|
||||||
|
// Output:
|
||||||
|
// [2 4]
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClearSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{"TestClearSlice_NonEmptySlice", []int{1, 2, 3}, []int{}},
|
||||||
|
{"TestClearSlice_EmptySlice", []int{}, []int{}},
|
||||||
|
{"TestClearSlice_NilSlice", nil, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
collection.ClearSlice(&c.input)
|
||||||
|
if len(c.input) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the length of input is not equal")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(c.input); i++ {
|
||||||
|
if c.input[i] != c.expected[i] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the inputV of input is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClearMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
expected map[int]int
|
||||||
|
}{
|
||||||
|
{"TestClearMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{}},
|
||||||
|
{"TestClearMap_EmptyMap", map[int]int{}, map[int]int{}},
|
||||||
|
{"TestClearMap_NilMap", nil, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
collection.ClearMap(c.input)
|
||||||
|
if len(c.input) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the length of map is not equal")
|
||||||
|
}
|
||||||
|
for k, v := range c.input {
|
||||||
|
if v != c.expected[k] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the inputV of map is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDropSliceByIndices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
indices []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{"TestDropSliceByIndices_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{1, 3}, []int{1, 3, 5}},
|
||||||
|
{"TestDropSliceByIndices_EmptySlice", []int{}, []int{1, 3}, []int{}},
|
||||||
|
{"TestDropSliceByIndices_NilSlice", nil, []int{1, 3}, nil},
|
||||||
|
{"TestDropSliceByIndices_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{}, []int{1, 2, 3, 4, 5}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
collection.DropSliceByIndices(&c.input, c.indices...)
|
||||||
|
if len(c.input) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the length of input is not equal")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(c.input); i++ {
|
||||||
|
if c.input[i] != c.expected[i] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the inputV of input is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDropSliceByCondition(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
condition func(v int) bool
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{"TestDropSliceByCondition_NonEmptySlice", []int{1, 2, 3, 4, 5}, func(v int) bool { return v%2 == 0 }, []int{1, 3, 5}},
|
||||||
|
{"TestDropSliceByCondition_EmptySlice", []int{}, func(v int) bool { return v%2 == 0 }, []int{}},
|
||||||
|
{"TestDropSliceByCondition_NilSlice", nil, func(v int) bool { return v%2 == 0 }, nil},
|
||||||
|
{"TestDropSliceByCondition_NonEmptySlice", []int{1, 2, 3, 4, 5}, func(v int) bool { return v%2 == 1 }, []int{2, 4}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
collection.DropSliceByCondition(&c.input, c.condition)
|
||||||
|
if len(c.input) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the length of input is not equal")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(c.input); i++ {
|
||||||
|
if c.input[i] != c.expected[i] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the inputV of input is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDropSliceOverlappingElements(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
anotherSlice []int
|
||||||
|
comparisonHandler collection.ComparisonHandler[int]
|
||||||
|
expected []int
|
||||||
|
expectedAnother []int
|
||||||
|
expectedComparison []int
|
||||||
|
}{
|
||||||
|
{"TestDropSliceOverlappingElements_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{3, 4, 5, 6, 7}, func(v1, v2 int) bool { return v1 == v2 }, []int{1, 2}, []int{6, 7}, []int{3, 4, 5}},
|
||||||
|
{"TestDropSliceOverlappingElements_EmptySlice", []int{}, []int{3, 4, 5, 6, 7}, func(v1, v2 int) bool { return v1 == v2 }, []int{}, []int{3, 4, 5, 6, 7}, []int{}},
|
||||||
|
{"TestDropSliceOverlappingElements_NilSlice", nil, []int{3, 4, 5, 6, 7}, func(v1, v2 int) bool { return v1 == v2 }, nil, []int{3, 4, 5, 6, 7}, nil},
|
||||||
|
{"TestDropSliceOverlappingElements_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{}, func(v1, v2 int) bool { return v1 == v2 }, []int{1, 2, 3, 4, 5}, []int{}, []int{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
collection.DropSliceOverlappingElements(&c.input, c.anotherSlice, c.comparisonHandler)
|
||||||
|
if len(c.input) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the length of input is not equal")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(c.input); i++ {
|
||||||
|
if c.input[i] != c.expected[i] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the inputV of input is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package sher
|
package collection
|
||||||
|
|
||||||
// DeduplicateSliceInPlace 去除切片中的重复元素
|
// DeduplicateSliceInPlace 去除切片中的重复元素
|
||||||
func DeduplicateSliceInPlace[S ~[]V, V comparable](s *S) {
|
func DeduplicateSliceInPlace[S ~[]V, V comparable](s *S) {
|
|
@ -0,0 +1,37 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func ExampleDeduplicateSliceInPlace() {
|
||||||
|
slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1}
|
||||||
|
DeduplicateSliceInPlace(&slice)
|
||||||
|
fmt.Println(slice)
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDeduplicateSlice() {
|
||||||
|
slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1}
|
||||||
|
fmt.Println(DeduplicateSlice(slice))
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDeduplicateSliceInPlaceWithCompare() {
|
||||||
|
slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1}
|
||||||
|
DeduplicateSliceInPlaceWithCompare(&slice, func(a, b int) bool {
|
||||||
|
return a == b
|
||||||
|
})
|
||||||
|
fmt.Println(slice)
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDeduplicateSliceWithCompare() {
|
||||||
|
slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1}
|
||||||
|
fmt.Println(DeduplicateSliceWithCompare(slice, func(a, b int) bool {
|
||||||
|
return a == b
|
||||||
|
}))
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5]
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeduplicateSliceInPlace(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{name: "TestDeduplicateSliceInPlace_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}},
|
||||||
|
{name: "TestDeduplicateSliceInPlace_Empty", input: []int{}, expected: []int{}},
|
||||||
|
{name: "TestDeduplicateSliceInPlace_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
collection.DeduplicateSliceInPlace(&c.input)
|
||||||
|
if len(c.input) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(c.input); i++ {
|
||||||
|
av, ev := c.input[i], c.expected[i]
|
||||||
|
if av != ev {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeduplicateSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{name: "TestDeduplicateSlice_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}},
|
||||||
|
{name: "TestDeduplicateSlice_Empty", input: []int{}, expected: []int{}},
|
||||||
|
{name: "TestDeduplicateSlice_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.DeduplicateSlice(c.input)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(actual); i++ {
|
||||||
|
av, ev := actual[i], c.expected[i]
|
||||||
|
if av != ev {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeduplicateSliceInPlaceWithCompare(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{name: "TestDeduplicateSliceInPlaceWithCompare_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}},
|
||||||
|
{name: "TestDeduplicateSliceInPlaceWithCompare_Empty", input: []int{}, expected: []int{}},
|
||||||
|
{name: "TestDeduplicateSliceInPlaceWithCompare_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
collection.DeduplicateSliceInPlaceWithCompare(&c.input, func(a, b int) bool {
|
||||||
|
return a == b
|
||||||
|
})
|
||||||
|
if len(c.input) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(c.input); i++ {
|
||||||
|
av, ev := c.input[i], c.expected[i]
|
||||||
|
if av != ev {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, c.input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeduplicateSliceWithCompare(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{name: "TestDeduplicateSliceWithCompare_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}},
|
||||||
|
{name: "TestDeduplicateSliceWithCompare_Empty", input: []int{}, expected: []int{}},
|
||||||
|
{name: "TestDeduplicateSliceWithCompare_Nil", input: nil, expected: nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.DeduplicateSliceWithCompare(c.input, func(a, b int) bool {
|
||||||
|
return a == b
|
||||||
|
})
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(actual); i++ {
|
||||||
|
av, ev := actual[i], c.expected[i]
|
||||||
|
if av != ev {
|
||||||
|
t.Errorf("expected: %v, actual: %v", c.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,20 @@
|
||||||
package sher
|
package collection
|
||||||
|
|
||||||
// FilterOutByIndices 过滤切片中特定索引的元素,返回过滤后的切片
|
// FilterOutByIndices 过滤切片中特定索引的元素,返回过滤后的切片
|
||||||
func FilterOutByIndices[S ~[]V, V any](slice S, indices ...int) S {
|
func FilterOutByIndices[S []V, V any](slice S, indices ...int) S {
|
||||||
if slice == nil {
|
if slice == nil || len(slice) == 0 || len(indices) == 0 {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(indices) == 0 {
|
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
excludeMap := make(map[int]bool)
|
excludeMap := make(map[int]bool)
|
||||||
for _, ex := range indices {
|
for _, ex := range indices {
|
||||||
excludeMap[ex] = true
|
if ex >= 0 && ex < len(slice) {
|
||||||
|
excludeMap[ex] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(excludeMap) == 0 {
|
||||||
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
validElements := make([]V, 0, len(slice)-len(excludeMap))
|
validElements := make([]V, 0, len(slice)-len(excludeMap))
|
||||||
|
@ -97,7 +100,7 @@ func FilterOutByKeys[M ~map[K]V, K comparable, V any](m M, keys ...K) M {
|
||||||
return validMap
|
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 {
|
func FilterOutByValues[M ~map[K]V, K comparable, V any](m M, values []V, handler ComparisonHandler[V]) M {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return 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))
|
validMap := make(M, len(m))
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
if condition(k, v) {
|
if !condition(k, v) {
|
||||||
validMap[k] = v
|
validMap[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleFilterOutByIndices() {
|
||||||
|
var slice = []int{1, 2, 3, 4, 5}
|
||||||
|
var result = collection.FilterOutByIndices(slice, 1, 3)
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// [1 3 5]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFilterOutByCondition() {
|
||||||
|
var slice = []int{1, 2, 3, 4, 5}
|
||||||
|
var result = collection.FilterOutByCondition(slice, func(v int) bool {
|
||||||
|
return v%2 == 0
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// [1 3 5]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFilterOutByKey() {
|
||||||
|
var m = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||||
|
var result = collection.FilterOutByKey(m, "b")
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// map[a:1 c:3]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFilterOutByValue() {
|
||||||
|
var m = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||||
|
var result = collection.FilterOutByValue(m, 2, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
fmt.Println(len(result))
|
||||||
|
// Output:
|
||||||
|
// 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFilterOutByKeys() {
|
||||||
|
var m = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||||
|
var result = collection.FilterOutByKeys(m, "a", "c")
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// map[b:2]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFilterOutByValues() {
|
||||||
|
var m = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||||
|
var result = collection.FilterOutByValues(m, []int{1}, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
for i, s := range []string{"a", "b", "c"} {
|
||||||
|
fmt.Println(i, result[s])
|
||||||
|
}
|
||||||
|
// Output:
|
||||||
|
// 0 0
|
||||||
|
// 1 2
|
||||||
|
// 2 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFilterOutByMap() {
|
||||||
|
var m = map[string]int{"a": 1, "b": 2, "c": 3}
|
||||||
|
var result = collection.FilterOutByMap(m, func(k string, v int) bool {
|
||||||
|
return k == "a" || v == 3
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// map[b:2]
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFilterOutByIndices(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
indices []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{"TestFilterOutByIndices_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{1, 3}, []int{1, 3, 5}},
|
||||||
|
{"TestFilterOutByIndices_EmptySlice", []int{}, []int{1, 3}, []int{}},
|
||||||
|
{"TestFilterOutByIndices_NilSlice", nil, []int{1, 3}, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FilterOutByIndices(c.input, c.indices...)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of input is not equal")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(actual); i++ {
|
||||||
|
if actual[i] != c.expected[i] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of input is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterOutByCondition(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
condition func(int) bool
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{"TestFilterOutByCondition_NonEmptySlice", []int{1, 2, 3, 4, 5}, func(v int) bool {
|
||||||
|
return v%2 == 0
|
||||||
|
}, []int{1, 3, 5}},
|
||||||
|
{"TestFilterOutByCondition_EmptySlice", []int{}, func(v int) bool {
|
||||||
|
return v%2 == 0
|
||||||
|
}, []int{}},
|
||||||
|
{"TestFilterOutByCondition_NilSlice", nil, func(v int) bool {
|
||||||
|
return v%2 == 0
|
||||||
|
}, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FilterOutByCondition(c.input, c.condition)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of input is not equal")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(actual); i++ {
|
||||||
|
if actual[i] != c.expected[i] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of input is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterOutByKey(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
key int
|
||||||
|
expected map[int]int
|
||||||
|
}{
|
||||||
|
{"TestFilterOutByKey_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 1, map[int]int{2: 2, 3: 3}},
|
||||||
|
{"TestFilterOutByKey_EmptyMap", map[int]int{}, 1, map[int]int{}},
|
||||||
|
{"TestFilterOutByKey_NilMap", nil, 1, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FilterOutByKey(c.input, c.key)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal")
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if v != c.expected[k] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterOutByValue(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
value int
|
||||||
|
expected map[int]int
|
||||||
|
}{
|
||||||
|
{"TestFilterOutByValue_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 1, map[int]int{2: 2, 3: 3}},
|
||||||
|
{"TestFilterOutByValue_EmptyMap", map[int]int{}, 1, map[int]int{}},
|
||||||
|
{"TestFilterOutByValue_NilMap", nil, 1, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FilterOutByValue(c.input, c.value, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal")
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if v != c.expected[k] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterOutByKeys(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
keys []int
|
||||||
|
expected map[int]int
|
||||||
|
}{
|
||||||
|
{"TestFilterOutByKeys_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, []int{1, 3}, map[int]int{2: 2}},
|
||||||
|
{"TestFilterOutByKeys_EmptyMap", map[int]int{}, []int{1, 3}, map[int]int{}},
|
||||||
|
{"TestFilterOutByKeys_NilMap", nil, []int{1, 3}, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FilterOutByKeys(c.input, c.keys...)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal")
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if v != c.expected[k] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterOutByValues(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
values []int
|
||||||
|
expected map[int]int
|
||||||
|
}{
|
||||||
|
{"TestFilterOutByValues_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, []int{1, 3}, map[int]int{2: 2}},
|
||||||
|
{"TestFilterOutByValues_EmptyMap", map[int]int{}, []int{1, 3}, map[int]int{}},
|
||||||
|
{"TestFilterOutByValues_NilMap", nil, []int{1, 3}, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FilterOutByValues(c.input, c.values, func(source, target int) bool {
|
||||||
|
return source == target
|
||||||
|
})
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal")
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if v != c.expected[k] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterOutByMap(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input map[int]int
|
||||||
|
filter map[int]int
|
||||||
|
expected map[int]int
|
||||||
|
}{
|
||||||
|
{"TestFilterOutByMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{1: 1, 3: 3}, map[int]int{2: 2}},
|
||||||
|
{"TestFilterOutByMap_EmptyMap", map[int]int{}, map[int]int{1: 1, 3: 3}, map[int]int{}},
|
||||||
|
{"TestFilterOutByMap_NilMap", nil, map[int]int{1: 1, 3: 3}, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FilterOutByMap(c.input, func(k int, v int) bool {
|
||||||
|
return c.filter[k] == v
|
||||||
|
})
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal")
|
||||||
|
}
|
||||||
|
for k, v := range actual {
|
||||||
|
if v != c.expected[k] {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,256 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import "github.com/kercylan98/minotaur/utils/generic"
|
||||||
|
|
||||||
|
// FindLoopedNextInSlice 返回 i 的下一个数组成员,当 i 达到数组长度时从 0 开始
|
||||||
|
// - 当 i 为负数时将返回第一个元素
|
||||||
|
func FindLoopedNextInSlice[S ~[]V, V any](slice S, i int) (next int, value V) {
|
||||||
|
if i < 0 {
|
||||||
|
return 0, slice[0]
|
||||||
|
}
|
||||||
|
next = i + 1
|
||||||
|
if next == len(slice) {
|
||||||
|
next = 0
|
||||||
|
}
|
||||||
|
return next, slice[next]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLoopedPrevInSlice 返回 i 的上一个数组成员,当 i 为 0 时从数组末尾开始
|
||||||
|
// - 当 i 为负数时将返回最后一个元素
|
||||||
|
func FindLoopedPrevInSlice[S ~[]V, V any](slice S, i int) (prev int, value V) {
|
||||||
|
if i < 0 {
|
||||||
|
return len(slice) - 1, slice[len(slice)-1]
|
||||||
|
}
|
||||||
|
prev = i - 1
|
||||||
|
if prev == -1 {
|
||||||
|
prev = len(slice) - 1
|
||||||
|
}
|
||||||
|
return prev, slice[prev]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindCombinationsInSliceByRange 获取给定数组的所有组合,且每个组合的成员数量限制在指定范围内
|
||||||
|
func FindCombinationsInSliceByRange[S ~[]V, V any](s S, minSize, maxSize int) []S {
|
||||||
|
n := len(s)
|
||||||
|
if n == 0 || minSize <= 0 || maxSize <= 0 || minSize > maxSize {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []S
|
||||||
|
var currentCombination S
|
||||||
|
|
||||||
|
var backtrack func(startIndex int, currentSize int)
|
||||||
|
backtrack = func(startIndex int, currentSize int) {
|
||||||
|
if currentSize >= minSize && currentSize <= maxSize {
|
||||||
|
combination := make(S, len(currentCombination))
|
||||||
|
copy(combination, currentCombination)
|
||||||
|
result = append(result, combination)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := startIndex; i < n; i++ {
|
||||||
|
currentCombination = append(currentCombination, s[i])
|
||||||
|
backtrack(i+1, currentSize+1)
|
||||||
|
currentCombination = currentCombination[:len(currentCombination)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrack(0, 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFirstOrDefaultInSlice 判断切片中是否存在元素,返回第一个元素,不存在则返回默认值
|
||||||
|
func FindFirstOrDefaultInSlice[S ~[]V, V any](slice S, defaultValue V) V {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return slice[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindOrDefaultInSlice 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则返回默认值
|
||||||
|
func FindOrDefaultInSlice[S ~[]V, V any](slice S, defaultValue V, handler func(v V) bool) (t V) {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
for _, v := range slice {
|
||||||
|
if handler(v) {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindOrDefaultInComparableSlice 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则返回默认值
|
||||||
|
func FindOrDefaultInComparableSlice[S ~[]V, V comparable](slice S, v V, defaultValue V) (t V) {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
for _, value := range slice {
|
||||||
|
if value == v {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindInSlice 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1
|
||||||
|
func FindInSlice[S ~[]V, V any](slice S, handler func(v V) bool) (i int, t V) {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return -1, t
|
||||||
|
}
|
||||||
|
for i, v := range slice {
|
||||||
|
if handler(v) {
|
||||||
|
return i, v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, t
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindIndexInSlice 判断切片中是否存在某个元素,返回第一个匹配的索引,不存在则索引返回 -1
|
||||||
|
func FindIndexInSlice[S ~[]V, V any](slice S, handler func(v V) bool) int {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
for i, v := range slice {
|
||||||
|
if handler(v) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindInComparableSlice 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1
|
||||||
|
func FindInComparableSlice[S ~[]V, V comparable](slice S, v V) (i int, t V) {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return -1, t
|
||||||
|
}
|
||||||
|
for i, value := range slice {
|
||||||
|
if value == v {
|
||||||
|
return i, value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, t
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindIndexInComparableSlice 判断切片中是否存在某个元素,返回第一个匹配的索引,不存在则索引返回 -1
|
||||||
|
func FindIndexInComparableSlice[S ~[]V, V comparable](slice S, v V) int {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
for i, value := range slice {
|
||||||
|
if value == v {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindInSliceByBinary 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1
|
||||||
|
func FindInSliceByBinary[S ~[]V, V any](slice S, handler func(v V) bool) (i int, t V) {
|
||||||
|
low := 0
|
||||||
|
high := len(slice) - 1
|
||||||
|
|
||||||
|
for low <= high {
|
||||||
|
mid := low + (high-low)/2
|
||||||
|
if handler(slice[mid]) {
|
||||||
|
return mid, slice[mid]
|
||||||
|
} else if handler(slice[mid]) {
|
||||||
|
high = mid - 1
|
||||||
|
} else {
|
||||||
|
low = mid + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, t
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMinimumInSlice 获取切片中的最小值
|
||||||
|
func FindMinimumInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (result V) {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result = slice[0]
|
||||||
|
for i := 1; i < len(slice); i++ {
|
||||||
|
if handler(slice[i], result) {
|
||||||
|
result = slice[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMaximumInSlice 获取切片中的最大值
|
||||||
|
func FindMaximumInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (result V) {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result = slice[0]
|
||||||
|
for i := 1; i < len(slice); i++ {
|
||||||
|
if handler(result, slice[i]) {
|
||||||
|
result = slice[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMin2MaxInSlice 获取切片中的最小值和最大值
|
||||||
|
func FindMin2MaxInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (min, max V) {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
min = slice[0]
|
||||||
|
max = slice[0]
|
||||||
|
for i := 1; i < len(slice); i++ {
|
||||||
|
if handler(slice[i], min) {
|
||||||
|
min = slice[i]
|
||||||
|
}
|
||||||
|
if handler(max, slice[i]) {
|
||||||
|
max = slice[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMinFromMap 获取 map 中的最小值
|
||||||
|
func FindMinFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (result V) {
|
||||||
|
if m == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, v := range m {
|
||||||
|
if handler(v, result) {
|
||||||
|
result = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMaxFromMap 获取 map 中的最大值
|
||||||
|
func FindMaxFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (result V) {
|
||||||
|
if m == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, v := range m {
|
||||||
|
if handler(result, v) {
|
||||||
|
result = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMin2MaxFromMap 获取 map 中的最小值和最大值
|
||||||
|
func FindMin2MaxFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (min, max V) {
|
||||||
|
if m == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, v := range m {
|
||||||
|
if handler(v, min) {
|
||||||
|
min = v
|
||||||
|
}
|
||||||
|
if handler(max, v) {
|
||||||
|
max = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindInMapKey 判断 map 中是否存在某个 key
|
||||||
|
func FindInMapKey[M ~map[K]V, K comparable, V any](m M, k K) bool {
|
||||||
|
_, exist := m[k]
|
||||||
|
return exist
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleFindLoopedNextInSlice() {
|
||||||
|
next, v := collection.FindLoopedNextInSlice([]int{1, 2, 3}, 1)
|
||||||
|
fmt.Println(next, v)
|
||||||
|
// Output:
|
||||||
|
// 2 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFindLoopedPrevInSlice() {
|
||||||
|
prev, v := collection.FindLoopedPrevInSlice([]int{1, 2, 3}, 1)
|
||||||
|
fmt.Println(prev, v)
|
||||||
|
// Output:
|
||||||
|
// 0 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFindCombinationsInSliceByRange() {
|
||||||
|
result := collection.FindCombinationsInSliceByRange([]int{1, 2, 3}, 1, 2)
|
||||||
|
fmt.Println(len(result))
|
||||||
|
// Output:
|
||||||
|
// 6
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFindFirstOrDefaultInSlice() {
|
||||||
|
result := collection.FindFirstOrDefaultInSlice([]int{1, 2, 3}, 0)
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFindOrDefaultInSlice() {
|
||||||
|
result := collection.FindOrDefaultInSlice([]int{1, 2, 3}, 0, func(v int) bool {
|
||||||
|
return v == 2
|
||||||
|
})
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFindOrDefaultInComparableSlice() {
|
||||||
|
result := collection.FindOrDefaultInComparableSlice([]int{1, 2, 3}, 2, 0)
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// 2
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package collection_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFindLoopedNextInSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
i int
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"TestFindLoopedNextInSlice_NonEmptySlice", []int{1, 2, 3}, 1, 2},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual, _ := collection.FindLoopedNextInSlice(c.input, c.i)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the next index of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindLoopedPrevInSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
i int
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"TestFindLoopedPrevInSlice_NonEmptySlice", []int{1, 2, 3}, 1, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual, _ := collection.FindLoopedPrevInSlice(c.input, c.i)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the prev index of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindCombinationsInSliceByRange(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
minSize int
|
||||||
|
maxSize int
|
||||||
|
expected [][]int
|
||||||
|
}{
|
||||||
|
{"TestFindCombinationsInSliceByRange_NonEmptySlice", []int{1, 2, 3}, 1, 2, [][]int{{1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FindCombinationsInSliceByRange(c.input, c.minSize, c.maxSize)
|
||||||
|
if len(actual) != len(c.expected) {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the length of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindFirstOrDefaultInSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"TestFindFirstOrDefaultInSlice_NonEmptySlice", []int{1, 2, 3}, 1},
|
||||||
|
{"TestFindFirstOrDefaultInSlice_EmptySlice", []int{}, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FindFirstOrDefaultInSlice(c.input, 0)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the first element of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindOrDefaultInSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"TestFindOrDefaultInSlice_NonEmptySlice", []int{1, 2, 3}, 2},
|
||||||
|
{"TestFindOrDefaultInSlice_EmptySlice", []int{}, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FindOrDefaultInSlice(c.input, 0, func(v int) bool {
|
||||||
|
return v == 2
|
||||||
|
})
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the element of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindOrDefaultInComparableSlice(t *testing.T) {
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
input []int
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"TestFindOrDefaultInComparableSlice_NonEmptySlice", []int{1, 2, 3}, 2},
|
||||||
|
{"TestFindOrDefaultInComparableSlice_EmptySlice", []int{}, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
actual := collection.FindOrDefaultInComparableSlice(c.input, 2, 0)
|
||||||
|
if actual != c.expected {
|
||||||
|
t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the element of input is not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
// SwapSlice 将切片中的两个元素进行交换
|
||||||
|
func SwapSlice[S ~[]V, V any](slice *S, i, j int) {
|
||||||
|
(*slice)[i], (*slice)[j] = (*slice)[j], (*slice)[i]
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package listings
|
||||||
|
|
||||||
|
// NewMatrix 创建一个新的 Matrix 实例。
|
||||||
|
func NewMatrix[V any](dimensions ...int) *Matrix[V] {
|
||||||
|
total := 1
|
||||||
|
for _, dim := range dimensions {
|
||||||
|
total *= dim
|
||||||
|
}
|
||||||
|
return &Matrix[V]{
|
||||||
|
dimensions: dimensions,
|
||||||
|
data: make([]V, total),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Matrix[V any] struct {
|
||||||
|
dimensions []int // 维度大小的切片
|
||||||
|
data []V // 存储矩阵数据的一维切片
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取矩阵中给定索引的元素。
|
||||||
|
func (slf *Matrix[V]) Get(index ...int) *V {
|
||||||
|
if len(index) != len(slf.dimensions) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset = 0
|
||||||
|
for i, dim := range slf.dimensions {
|
||||||
|
if index[i] < 0 || index[i] >= dim {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
offset = offset*dim + index[i]
|
||||||
|
}
|
||||||
|
return &slf.data[offset]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set 设置矩阵中给定索引的元素。
|
||||||
|
func (slf *Matrix[V]) Set(index []int, value V) {
|
||||||
|
if len(index) != len(slf.dimensions) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset = 0
|
||||||
|
for i, dim := range slf.dimensions {
|
||||||
|
if index[i] < 0 || index[i] >= dim {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
offset = offset*dim + index[i]
|
||||||
|
}
|
||||||
|
slf.data[offset] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dimensions 返回矩阵的维度大小。
|
||||||
|
func (slf *Matrix[V]) Dimensions() []int {
|
||||||
|
return slf.dimensions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear 清空矩阵。
|
||||||
|
func (slf *Matrix[V]) Clear() {
|
||||||
|
slf.data = make([]V, len(slf.data))
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package slice
|
package listings
|
||||||
|
|
||||||
// NewPagedSlice 创建一个新的 PagedSlice 实例。
|
// NewPagedSlice 创建一个新的 PagedSlice 实例。
|
||||||
func NewPagedSlice[T any](pageSize int) *PagedSlice[T] {
|
func NewPagedSlice[T any](pageSize int) *PagedSlice[T] {
|
|
@ -0,0 +1,198 @@
|
||||||
|
package listings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewPrioritySlice 创建一个优先级切片
|
||||||
|
func NewPrioritySlice[V any](lengthAndCap ...int) *PrioritySlice[V] {
|
||||||
|
p := &PrioritySlice[V]{}
|
||||||
|
if len(lengthAndCap) > 0 {
|
||||||
|
var length = lengthAndCap[0]
|
||||||
|
var c int
|
||||||
|
if len(lengthAndCap) > 1 {
|
||||||
|
c = lengthAndCap[1]
|
||||||
|
}
|
||||||
|
p.items = make([]*priorityItem[V], length, c)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrioritySlice 是一个优先级切片
|
||||||
|
type PrioritySlice[V any] struct {
|
||||||
|
items []*priorityItem[V]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len 返回切片长度
|
||||||
|
func (slf *PrioritySlice[V]) Len() int {
|
||||||
|
return len(slf.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cap 返回切片容量
|
||||||
|
func (slf *PrioritySlice[V]) Cap() int {
|
||||||
|
return cap(slf.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear 清空切片
|
||||||
|
func (slf *PrioritySlice[V]) Clear() {
|
||||||
|
slf.items = slf.items[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append 添加元素
|
||||||
|
func (slf *PrioritySlice[V]) Append(v V, p int) {
|
||||||
|
slf.items = append(slf.items, &priorityItem[V]{
|
||||||
|
v: v,
|
||||||
|
p: p,
|
||||||
|
})
|
||||||
|
slf.sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appends 添加元素
|
||||||
|
func (slf *PrioritySlice[V]) Appends(priority int, vs ...V) {
|
||||||
|
for _, v := range vs {
|
||||||
|
slf.Append(v, priority)
|
||||||
|
}
|
||||||
|
slf.sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取元素
|
||||||
|
func (slf *PrioritySlice[V]) Get(index int) (V, int) {
|
||||||
|
i := slf.items[index]
|
||||||
|
return i.Value(), i.Priority()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValue 获取元素值
|
||||||
|
func (slf *PrioritySlice[V]) GetValue(index int) V {
|
||||||
|
return slf.items[index].Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPriority 获取元素优先级
|
||||||
|
func (slf *PrioritySlice[V]) GetPriority(index int) int {
|
||||||
|
return slf.items[index].Priority()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set 设置元素
|
||||||
|
func (slf *PrioritySlice[V]) Set(index int, value V, priority int) {
|
||||||
|
before := slf.items[index]
|
||||||
|
slf.items[index] = &priorityItem[V]{
|
||||||
|
v: value,
|
||||||
|
p: priority,
|
||||||
|
}
|
||||||
|
if before.Priority() != priority {
|
||||||
|
slf.sort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValue 设置元素值
|
||||||
|
func (slf *PrioritySlice[V]) SetValue(index int, value V) {
|
||||||
|
slf.items[index].v = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPriority 设置元素优先级
|
||||||
|
func (slf *PrioritySlice[V]) SetPriority(index int, priority int) {
|
||||||
|
slf.items[index].p = priority
|
||||||
|
slf.sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action 直接操作切片,如果返回值不为 nil,则替换切片
|
||||||
|
func (slf *PrioritySlice[V]) Action(action func(items []*priorityItem[V]) []*priorityItem[V]) {
|
||||||
|
if len(slf.items) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if replace := action(slf.items); replace != nil {
|
||||||
|
slf.items = replace
|
||||||
|
slf.sort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range 遍历切片,如果返回值为 false,则停止遍历
|
||||||
|
func (slf *PrioritySlice[V]) Range(action func(index int, item *priorityItem[V]) bool) {
|
||||||
|
for i, item := range slf.items {
|
||||||
|
if !action(i, item) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RangeValue 遍历切片值,如果返回值为 false,则停止遍历
|
||||||
|
func (slf *PrioritySlice[V]) RangeValue(action func(index int, value V) bool) {
|
||||||
|
slf.Range(func(index int, item *priorityItem[V]) bool {
|
||||||
|
return action(index, item.Value())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RangePriority 遍历切片优先级,如果返回值为 false,则停止遍历
|
||||||
|
func (slf *PrioritySlice[V]) RangePriority(action func(index int, priority int) bool) {
|
||||||
|
slf.Range(func(index int, item *priorityItem[V]) bool {
|
||||||
|
return action(index, item.Priority())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice 返回切片
|
||||||
|
func (slf *PrioritySlice[V]) Slice() []V {
|
||||||
|
var vs []V
|
||||||
|
for _, item := range slf.items {
|
||||||
|
vs = append(vs, item.Value())
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
// String 返回切片字符串
|
||||||
|
func (slf *PrioritySlice[V]) String() string {
|
||||||
|
var vs []V
|
||||||
|
for _, item := range slf.items {
|
||||||
|
vs = append(vs, item.Value())
|
||||||
|
}
|
||||||
|
return fmt.Sprint(vs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort 排序
|
||||||
|
func (slf *PrioritySlice[V]) sort() {
|
||||||
|
if len(slf.items) <= 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sort.Slice(slf.items, func(i, j int) bool {
|
||||||
|
return slf.items[i].Priority() < slf.items[j].Priority()
|
||||||
|
})
|
||||||
|
for i := 0; i < len(slf.items); i++ {
|
||||||
|
if i == 0 {
|
||||||
|
slf.items[i].prev = nil
|
||||||
|
slf.items[i].next = slf.items[i+1]
|
||||||
|
} else if i == len(slf.items)-1 {
|
||||||
|
slf.items[i].prev = slf.items[i-1]
|
||||||
|
slf.items[i].next = nil
|
||||||
|
} else {
|
||||||
|
slf.items[i].prev = slf.items[i-1]
|
||||||
|
slf.items[i].next = slf.items[i+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// priorityItem 是一个优先级切片元素
|
||||||
|
type priorityItem[V any] struct {
|
||||||
|
next *priorityItem[V]
|
||||||
|
prev *priorityItem[V]
|
||||||
|
v V
|
||||||
|
p int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value 返回元素值
|
||||||
|
func (p *priorityItem[V]) Value() V {
|
||||||
|
return p.v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priority 返回元素优先级
|
||||||
|
func (p *priorityItem[V]) Priority() int {
|
||||||
|
return p.p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next 返回下一个元素
|
||||||
|
func (p *priorityItem[V]) Next() *priorityItem[V] {
|
||||||
|
return p.next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prev 返回上一个元素
|
||||||
|
func (p *priorityItem[V]) Prev() *priorityItem[V] {
|
||||||
|
return p.prev
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package listings_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection/listings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrioritySlice_Append(t *testing.T) {
|
||||||
|
var s = listings.NewPrioritySlice[string]()
|
||||||
|
s.Append("name_1", 2)
|
||||||
|
s.Append("name_2", 1)
|
||||||
|
fmt.Println(s)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package sher
|
package collection
|
||||||
|
|
||||||
// MappingFromSlice 将切片中的元素进行转换
|
// MappingFromSlice 将切片中的元素进行转换
|
||||||
func MappingFromSlice[S ~[]V, NS ~[]N, V, N any](slice S, handler func(value V) N) NS {
|
func MappingFromSlice[S ~[]V, NS ~[]N, V, N any](slice S, handler func(value V) N) NS {
|
|
@ -1,4 +1,4 @@
|
||||||
package sher
|
package collection
|
||||||
|
|
||||||
// MergeSlices 合并切片
|
// MergeSlices 合并切片
|
||||||
func MergeSlices[S ~[]V, V any](slices ...S) (result S) {
|
func MergeSlices[S ~[]V, V any](slices ...S) (result S) {
|
|
@ -1,18 +1,18 @@
|
||||||
package sher
|
package collection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kercylan98/minotaur/utils/random"
|
"github.com/kercylan98/minotaur/utils/random"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChooseRandomSliceElementRepeatN 获取切片中的 n 个随机元素,允许重复
|
// ChooseRandomSliceElementRepeatN 获取切片中的 inputN 个随机元素,允许重复
|
||||||
// - 如果 n 大于切片长度或小于 0 时将会发生 panic
|
// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic
|
||||||
func ChooseRandomSliceElementRepeatN[S ~[]V, V any](slice S, n int) (result []V) {
|
func ChooseRandomSliceElementRepeatN[S ~[]V, V any](slice S, n int) (result []V) {
|
||||||
if slice == nil {
|
if slice == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n > len(slice) || n < 0 {
|
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)
|
result = make([]V, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
|
@ -21,14 +21,14 @@ func ChooseRandomSliceElementRepeatN[S ~[]V, V any](slice S, n int) (result []V)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseRandomIndexRepeatN 获取切片中的 n 个随机元素的索引,允许重复
|
// ChooseRandomIndexRepeatN 获取切片中的 inputN 个随机元素的索引,允许重复
|
||||||
// - 如果 n 大于切片长度或小于 0 时将会发生 panic
|
// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic
|
||||||
func ChooseRandomIndexRepeatN[S ~[]V, V any](slice S, n int) (result []int) {
|
func ChooseRandomIndexRepeatN[S ~[]V, V any](slice S, n int) (result []int) {
|
||||||
if slice == nil {
|
if slice == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n > len(slice) || n < 0 {
|
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)
|
result = make([]int, n)
|
||||||
for i := 0; i < n; i++ {
|
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)
|
return random.Int(0, len(slice)-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseRandomSliceElementN 获取切片中的 n 个随机元素
|
// ChooseRandomSliceElementN 获取切片中的 inputN 个随机元素
|
||||||
// - 如果 n 大于切片长度或小于 0 时将会发生 panic
|
// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic
|
||||||
func ChooseRandomSliceElementN[S ~[]V, V any](slice S, n int) (result []V) {
|
func ChooseRandomSliceElementN[S ~[]V, V any](slice S, n int) (result []V) {
|
||||||
if slice == nil {
|
if slice == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n > len(slice) || n < 0 {
|
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)
|
result = make([]V, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
|
@ -69,14 +69,14 @@ func ChooseRandomSliceElementN[S ~[]V, V any](slice S, n int) (result []V) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseRandomIndexN 获取切片中的 n 个随机元素的索引
|
// ChooseRandomIndexN 获取切片中的 inputN 个随机元素的索引
|
||||||
// - 如果 n 大于切片长度或小于 0 时将会发生 panic
|
// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic
|
||||||
func ChooseRandomIndexN[S ~[]V, V any](slice S, n int) (result []int) {
|
func ChooseRandomIndexN[S ~[]V, V any](slice S, n int) (result []int) {
|
||||||
if slice == nil {
|
if slice == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n > len(slice) || n < 0 {
|
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)
|
result = make([]int, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
|
@ -85,14 +85,14 @@ func ChooseRandomIndexN[S ~[]V, V any](slice S, n int) (result []int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseRandomMapKeyRepeatN 获取 map 中的 n 个随机 key,允许重复
|
// ChooseRandomMapKeyRepeatN 获取 map 中的 inputN 个随机 key,允许重复
|
||||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||||
func ChooseRandomMapKeyRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (result []K) {
|
func ChooseRandomMapKeyRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (result []K) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n > len(m) || n < 0 {
|
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)
|
result = make([]K, n)
|
||||||
for i := 0; i < n; i++ {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseRandomMapValueRepeatN 获取 map 中的 n 个随机 value,允许重复
|
// ChooseRandomMapValueRepeatN 获取 map 中的 inputN 个随机 inputV,允许重复
|
||||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||||
func ChooseRandomMapValueRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (result []V) {
|
func ChooseRandomMapValueRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (result []V) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n > len(m) || n < 0 {
|
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)
|
result = make([]V, n)
|
||||||
for i := 0; i < n; i++ {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseRandomMapKeyAndValueRepeatN 获取 map 中的 n 个随机 key 和 value,允许重复
|
// ChooseRandomMapKeyAndValueRepeatN 获取 map 中的 inputN 个随机 key 和 inputV,允许重复
|
||||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||||
func ChooseRandomMapKeyAndValueRepeatN[M ~map[K]V, K comparable, V any](m M, n int) M {
|
func ChooseRandomMapKeyAndValueRepeatN[M ~map[K]V, K comparable, V any](m M, n int) M {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if n > len(m) || n < 0 {
|
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)
|
result := make(M, n)
|
||||||
for i := 0; i < n; i++ {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseRandomMapValue 获取 map 中的随机 value
|
// ChooseRandomMapValue 获取 map 中的随机 inputV
|
||||||
func ChooseRandomMapValue[M ~map[K]V, K comparable, V any](m M) (v V) {
|
func ChooseRandomMapValue[M ~map[K]V, K comparable, V any](m M) (v V) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return
|
return
|
||||||
|
@ -164,14 +164,14 @@ func ChooseRandomMapValue[M ~map[K]V, K comparable, V any](m M) (v V) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseRandomMapKeyN 获取 map 中的 n 个随机 key
|
// ChooseRandomMapKeyN 获取 map 中的 inputN 个随机 key
|
||||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||||
func ChooseRandomMapKeyN[M ~map[K]V, K comparable, V any](m M, n int) (result []K) {
|
func ChooseRandomMapKeyN[M ~map[K]V, K comparable, V any](m M, n int) (result []K) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n > len(m) || n < 0 {
|
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)
|
result = make([]K, n)
|
||||||
i := 0
|
i := 0
|
||||||
|
@ -185,14 +185,14 @@ func ChooseRandomMapKeyN[M ~map[K]V, K comparable, V any](m M, n int) (result []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseRandomMapValueN 获取 map 中的 n 个随机 value
|
// ChooseRandomMapValueN 获取 map 中的 inputN 个随机 inputV
|
||||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||||
func ChooseRandomMapValueN[M ~map[K]V, K comparable, V any](m M, n int) (result []V) {
|
func ChooseRandomMapValueN[M ~map[K]V, K comparable, V any](m M, n int) (result []V) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n > len(m) || n < 0 {
|
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)
|
result = make([]V, n)
|
||||||
i := 0
|
i := 0
|
||||||
|
@ -206,7 +206,7 @@ func ChooseRandomMapValueN[M ~map[K]V, K comparable, V any](m M, n int) (result
|
||||||
return
|
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) {
|
func ChooseRandomMapKeyAndValue[M ~map[K]V, K comparable, V any](m M) (k K, v V) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return
|
return
|
||||||
|
@ -217,14 +217,14 @@ func ChooseRandomMapKeyAndValue[M ~map[K]V, K comparable, V any](m M) (k K, v V)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChooseRandomMapKeyAndValueN 获取 map 中的 n 个随机 key 和 value
|
// ChooseRandomMapKeyAndValueN 获取 map 中的 inputN 个随机 key 和 inputV
|
||||||
// - 如果 n 大于 map 长度或小于 0 时将会发生 panic
|
// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic
|
||||||
func ChooseRandomMapKeyAndValueN[M ~map[K]V, K comparable, V any](m M, n int) M {
|
func ChooseRandomMapKeyAndValueN[M ~map[K]V, K comparable, V any](m M, n int) M {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if n > len(m) || n < 0 {
|
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)
|
result := make(M, n)
|
||||||
i := 0
|
i := 0
|
|
@ -1,4 +1,4 @@
|
||||||
package sher
|
package collection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
|
@ -6,6 +6,16 @@ import (
|
||||||
"sort"
|
"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 对切片进行降序排序
|
// Desc 对切片进行降序排序
|
||||||
func Desc[S ~[]V, V any, Sort generic.Ordered](slice *S, getter func(index int) Sort) {
|
func Desc[S ~[]V, V any, Sort generic.Ordered](slice *S, getter func(index int) Sort) {
|
||||||
sort.Slice(*slice, func(i, j int) bool {
|
sort.Slice(*slice, func(i, j int) bool {
|
|
@ -1,8 +1,8 @@
|
||||||
package combination
|
package combination
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
@ -24,7 +24,7 @@ func WithMatcherEvaluation[T Item](evaluate func(items []T) float64) MatcherOpti
|
||||||
func WithMatcherLeastLength[T Item](length int) MatcherOption[T] {
|
func WithMatcherLeastLength[T Item](length int) MatcherOption[T] {
|
||||||
return func(m *Matcher[T]) {
|
return func(m *Matcher[T]) {
|
||||||
m.AddFilter(func(items []T) [][]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] {
|
func WithMatcherLength[T Item](length int) MatcherOption[T] {
|
||||||
return func(m *Matcher[T]) {
|
return func(m *Matcher[T]) {
|
||||||
m.AddFilter(func(items []T) [][]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] {
|
func WithMatcherMostLength[T Item](length int) MatcherOption[T] {
|
||||||
return func(m *Matcher[T]) {
|
return func(m *Matcher[T]) {
|
||||||
m.AddFilter(func(items []T) [][]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] {
|
func WithMatcherIntervalLength[T Item](min, max int) MatcherOption[T] {
|
||||||
return func(m *Matcher[T]) {
|
return func(m *Matcher[T]) {
|
||||||
m.AddFilter(func(items []T) [][]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]) {
|
return func(m *Matcher[T]) {
|
||||||
m.AddFilter(func(items []T) [][]T {
|
m.AddFilter(func(items []T) [][]T {
|
||||||
var combinations [][]T
|
var combinations [][]T
|
||||||
groups := slice.LimitedCombinations(items, count, count)
|
groups := collection.FindCombinationsInSliceByRange(items, count, count)
|
||||||
for _, items := range groups {
|
for _, items := range groups {
|
||||||
if count > 0 && len(items) != count {
|
if count > 0 && len(items) != count {
|
||||||
continue
|
continue
|
||||||
|
@ -147,8 +147,12 @@ func WithMatcherNCarryM[T Item, E generic.Ordered](n, m int, getType func(item T
|
||||||
if len(group) != n {
|
if len(group) != n {
|
||||||
continue
|
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 {
|
for _, otherGroup := range ms {
|
||||||
var t E
|
var t E
|
||||||
var init = true
|
var init = true
|
||||||
|
@ -163,7 +167,7 @@ func WithMatcherNCarryM[T Item, E generic.Ordered](n, m int, getType func(item T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if same {
|
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 {
|
if len(group) != n {
|
||||||
continue
|
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 {
|
for _, otherGroup := range ms {
|
||||||
combinations = append(combinations, slice.Merge(group, otherGroup))
|
combinations = append(combinations, collection.MergeSlices(group, otherGroup))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return combinations
|
return combinations
|
||||||
|
|
|
@ -41,7 +41,7 @@ func ZIPUnCompress(dataByte []byte) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rc.Close()
|
_ = rc.Close()
|
||||||
}
|
}
|
||||||
return result.Bytes(), nil
|
return result.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package concurrent
|
package concurrent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,5 +55,5 @@ func (slf *Slice[T]) Clear() {
|
||||||
func (slf *Slice[T]) GetData() []T {
|
func (slf *Slice[T]) GetData() []T {
|
||||||
slf.rw.Lock()
|
slf.rw.Lock()
|
||||||
defer slf.rw.Unlock()
|
defer slf.rw.Unlock()
|
||||||
return slice.Copy(slf.data)
|
return collection.CloneSlice(slf.data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,214 +0,0 @@
|
||||||
package counter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewCounter 创建一个计数器
|
|
||||||
func NewCounter[K comparable, V generic.Number]() *Counter[K, V] {
|
|
||||||
c := &Counter[K, V]{
|
|
||||||
c: make(map[K]any),
|
|
||||||
dr: make(map[K]int64),
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// newSubCounter 创建一个子计数器
|
|
||||||
func newSubCounter[K comparable, V generic.Number]() *Counter[K, V] {
|
|
||||||
c := &Counter[K, V]{
|
|
||||||
c: make(map[K]any),
|
|
||||||
dr: make(map[K]int64),
|
|
||||||
sub: true,
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Counter 计数器
|
|
||||||
type Counter[K comparable, V generic.Number] struct {
|
|
||||||
lock sync.RWMutex
|
|
||||||
sub bool
|
|
||||||
dr map[K]int64
|
|
||||||
drm map[string]int64
|
|
||||||
c map[K]any
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddWithMark 添加计数,根据 mark 判断是否重复计数
|
|
||||||
func (slf *Counter[K, V]) AddWithMark(mark, key K, value V, expired time.Duration) {
|
|
||||||
slf.lock.Lock()
|
|
||||||
if expired > 0 {
|
|
||||||
now := time.Now().Unix()
|
|
||||||
mk := fmt.Sprintf("%v_%v", mark, key)
|
|
||||||
expiredTime, exist := slf.drm[mk]
|
|
||||||
if exist {
|
|
||||||
if expiredTime > now {
|
|
||||||
slf.lock.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slf.drm[mk] = now + int64(expired/time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
v, exist := slf.c[key]
|
|
||||||
if !exist {
|
|
||||||
slf.c[key] = value
|
|
||||||
slf.lock.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v, ok := v.(V); !ok {
|
|
||||||
slf.lock.Unlock()
|
|
||||||
panic("counter value is sub counter")
|
|
||||||
} else {
|
|
||||||
slf.c[key] = v + value
|
|
||||||
}
|
|
||||||
slf.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add 添加计数
|
|
||||||
// - 当设置了 expired 时,在 expired 时间内,不会重复计数
|
|
||||||
// - 需要注意的是,当第一次设置了 expired,第二次未设置时,第二次的计数将生效
|
|
||||||
func (slf *Counter[K, V]) Add(key K, value V, expired ...time.Duration) {
|
|
||||||
slf.lock.Lock()
|
|
||||||
if len(expired) > 0 && expired[0] > 0 {
|
|
||||||
now := time.Now().Unix()
|
|
||||||
expiredTime, exist := slf.dr[key]
|
|
||||||
if exist {
|
|
||||||
if expiredTime > now {
|
|
||||||
slf.lock.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slf.dr[key] = now + int64(expired[0]/time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
v, exist := slf.c[key]
|
|
||||||
if !exist {
|
|
||||||
slf.c[key] = value
|
|
||||||
slf.lock.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v, ok := v.(V); !ok {
|
|
||||||
slf.lock.Unlock()
|
|
||||||
panic("counter value is sub counter")
|
|
||||||
} else {
|
|
||||||
slf.c[key] = v + value
|
|
||||||
}
|
|
||||||
slf.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get 获取计数
|
|
||||||
func (slf *Counter[K, V]) Get(key K) V {
|
|
||||||
slf.lock.RLock()
|
|
||||||
v, exist := slf.c[key]
|
|
||||||
if !exist {
|
|
||||||
slf.lock.RUnlock()
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if v, ok := v.(V); !ok {
|
|
||||||
slf.lock.RUnlock()
|
|
||||||
panic("counter value is sub counter")
|
|
||||||
} else {
|
|
||||||
slf.lock.RUnlock()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset 重置特定 key 的计数
|
|
||||||
// - 当 key 为一个子计数器时,将会重置该子计数器
|
|
||||||
func (slf *Counter[K, V]) Reset(key K) {
|
|
||||||
slf.lock.Lock()
|
|
||||||
delete(slf.c, key)
|
|
||||||
delete(slf.dr, key)
|
|
||||||
slf.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetMark 重置特定 mark 的 key 的计数
|
|
||||||
func (slf *Counter[K, V]) ResetMark(mark, key K) {
|
|
||||||
slf.lock.Lock()
|
|
||||||
mk := fmt.Sprintf("%v_%v", mark, key)
|
|
||||||
delete(slf.c, key)
|
|
||||||
delete(slf.drm, mk)
|
|
||||||
slf.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetExpired 重置特定 key 的过期时间
|
|
||||||
func (slf *Counter[K, V]) ResetExpired(key K) {
|
|
||||||
slf.lock.Lock()
|
|
||||||
delete(slf.dr, key)
|
|
||||||
slf.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetExpiredMark 重置特定 mark 的 key 的过期时间
|
|
||||||
func (slf *Counter[K, V]) ResetExpiredMark(mark, key K) {
|
|
||||||
slf.lock.Lock()
|
|
||||||
mk := fmt.Sprintf("%v_%v", mark, key)
|
|
||||||
delete(slf.drm, mk)
|
|
||||||
slf.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetAll 重置所有计数
|
|
||||||
func (slf *Counter[K, V]) ResetAll() {
|
|
||||||
slf.lock.Lock()
|
|
||||||
hash.Clear(slf.c)
|
|
||||||
hash.Clear(slf.dr)
|
|
||||||
hash.Clear(slf.drm)
|
|
||||||
slf.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubCounter 获取子计数器
|
|
||||||
func (slf *Counter[K, V]) SubCounter(key K) *Counter[K, V] {
|
|
||||||
slf.lock.Lock()
|
|
||||||
v, exist := slf.c[key]
|
|
||||||
if !exist {
|
|
||||||
counter := newSubCounter[K, V]()
|
|
||||||
slf.c[key] = counter
|
|
||||||
slf.lock.Unlock()
|
|
||||||
return counter
|
|
||||||
}
|
|
||||||
if v, ok := v.(*Counter[K, V]); !ok {
|
|
||||||
slf.lock.Unlock()
|
|
||||||
panic("counter value is count value")
|
|
||||||
} else {
|
|
||||||
slf.lock.Unlock()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCounts 获取计数器的所有计数
|
|
||||||
func (slf *Counter[K, V]) GetCounts() map[K]V {
|
|
||||||
counts := make(map[K]V)
|
|
||||||
slf.lock.RLock()
|
|
||||||
for k, v := range slf.c {
|
|
||||||
if v, ok := v.(V); !ok {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
counts[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slf.lock.RUnlock()
|
|
||||||
return counts
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSubCounters 获取计数器的所有子计数器
|
|
||||||
func (slf *Counter[K, V]) GetSubCounters() map[K]*Counter[K, V] {
|
|
||||||
counters := make(map[K]*Counter[K, V])
|
|
||||||
slf.lock.RLock()
|
|
||||||
for k, v := range slf.c {
|
|
||||||
if v, ok := v.(*Counter[K, V]); !ok {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
counters[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slf.lock.RUnlock()
|
|
||||||
return counters
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shadow 获取该计数器的影子计数器,影子计数器的任何操作都不会影响到原计数器
|
|
||||||
// - 适用于持久化等场景
|
|
||||||
func (slf *Counter[K, V]) Shadow() *Shadow[K, V] {
|
|
||||||
return newShadow(slf)
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package counter_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/utils/counter"
|
|
||||||
"github.com/kercylan98/minotaur/utils/times"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCounter_Add(t *testing.T) {
|
|
||||||
c := counter.NewCounter[string, int64]()
|
|
||||||
|
|
||||||
c.Add("login_count", 1, times.GetNextDayInterval(time.Now()))
|
|
||||||
c.Add("login_count", 1, times.GetNextDayInterval(time.Now()))
|
|
||||||
c.SubCounter("live").Add("login_count", 1, times.GetNextDayInterval(time.Now()))
|
|
||||||
|
|
||||||
fmt.Println(c)
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package counter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newShadow[K comparable, V generic.Number](counter *Counter[K, V]) *Shadow[K, V] {
|
|
||||||
counter.lock.RLock()
|
|
||||||
shadow := &Shadow[K, V]{
|
|
||||||
Sub: counter.sub,
|
|
||||||
DeduplicationRecord: hash.Copy(counter.dr),
|
|
||||||
DeduplicationRecordMark: hash.Copy(counter.drm),
|
|
||||||
}
|
|
||||||
counter.lock.RUnlock()
|
|
||||||
shadow.Counter = counter.GetCounts()
|
|
||||||
subs := counter.GetSubCounters()
|
|
||||||
for k, c := range subs {
|
|
||||||
shadow.SubCounters[k] = newShadow(c)
|
|
||||||
}
|
|
||||||
return shadow
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shadow 计数器的影子计数器
|
|
||||||
type Shadow[K comparable, V generic.Number] struct {
|
|
||||||
Sub bool // 是否为子计数器
|
|
||||||
DeduplicationRecord map[K]int64 // 最后一次写入时间
|
|
||||||
DeduplicationRecordMark map[string]int64 // 最后一次写入时间
|
|
||||||
Counter map[K]V // 计数器
|
|
||||||
SubCounters map[K]*Shadow[K, V] // 子计数器
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToCounter 将影子计数器转换为计数器
|
|
||||||
func (slf *Shadow[K, V]) ToCounter() *Counter[K, V] {
|
|
||||||
return slf.toCounter(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// toCounter 将影子计数器转换为计数器
|
|
||||||
func (slf *Shadow[K, V]) toCounter(parent *Counter[K, V]) *Counter[K, V] {
|
|
||||||
counter := &Counter[K, V]{
|
|
||||||
sub: slf.Sub,
|
|
||||||
c: map[K]any{},
|
|
||||||
}
|
|
||||||
if slf.Sub {
|
|
||||||
counter.dr = parent.dr
|
|
||||||
} else {
|
|
||||||
counter.dr = hash.Copy(slf.DeduplicationRecord)
|
|
||||||
counter.drm = hash.Copy(slf.DeduplicationRecordMark)
|
|
||||||
}
|
|
||||||
for k, v := range slf.Counter {
|
|
||||||
counter.c[k] = v
|
|
||||||
}
|
|
||||||
for k, s := range slf.SubCounters {
|
|
||||||
counter.c[k] = s.toCounter(counter)
|
|
||||||
}
|
|
||||||
return counter
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package counter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewSimpleDeduplication 创建一个简单去重计数器
|
|
||||||
// - 该计数器不会记录每个 key 的计数,只会记录 key 的存在与否
|
|
||||||
// - 当 key 不存在时,计数器会将 key 记录为存在,并将计数器增加特定的值
|
|
||||||
func NewSimpleDeduplication[K comparable, V generic.Number]() *SimpleDeduplication[K, V] {
|
|
||||||
return &SimpleDeduplication[K, V]{
|
|
||||||
r: make(map[K]struct{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimpleDeduplication 简单去重计数器
|
|
||||||
type SimpleDeduplication[K comparable, V generic.Number] struct {
|
|
||||||
c V
|
|
||||||
r map[K]struct{}
|
|
||||||
l sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add 添加计数,根据 key 判断是否重复计数
|
|
||||||
func (slf *SimpleDeduplication[K, V]) Add(key K, value V) {
|
|
||||||
slf.l.Lock()
|
|
||||||
defer slf.l.Unlock()
|
|
||||||
if _, exist := slf.r[key]; !exist {
|
|
||||||
slf.r[key] = struct{}{}
|
|
||||||
slf.c += value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get 获取计数
|
|
||||||
func (slf *SimpleDeduplication[K, V]) Get() V {
|
|
||||||
slf.l.RLock()
|
|
||||||
defer slf.l.RUnlock()
|
|
||||||
return slf.c
|
|
||||||
}
|
|
|
@ -1,8 +1,7 @@
|
||||||
package deck
|
package deck
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDeck 创建一个新的甲板
|
// NewDeck 创建一个新的甲板
|
||||||
|
@ -21,7 +20,7 @@ type Deck[I Item] struct {
|
||||||
|
|
||||||
// AddGroup 将一个组添加到甲板中
|
// AddGroup 将一个组添加到甲板中
|
||||||
func (slf *Deck[I]) AddGroup(group *Group[I]) {
|
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.groups[group.GetGuid()] = group
|
||||||
slf.sort = append(slf.sort, group.GetGuid())
|
slf.sort = append(slf.sort, group.GetGuid())
|
||||||
}
|
}
|
||||||
|
@ -30,7 +29,7 @@ func (slf *Deck[I]) AddGroup(group *Group[I]) {
|
||||||
// RemoveGroup 移除甲板中的一个组
|
// RemoveGroup 移除甲板中的一个组
|
||||||
func (slf *Deck[I]) RemoveGroup(guid int64) {
|
func (slf *Deck[I]) RemoveGroup(guid int64) {
|
||||||
delete(slf.groups, guid)
|
delete(slf.groups, guid)
|
||||||
index := slice.GetIndex(slf.sort, guid)
|
index := collection.FindIndexInComparableSlice(slf.sort, guid)
|
||||||
if index != -1 {
|
if index != -1 {
|
||||||
slf.sort = append(slf.sort[:index], slf.sort[index+1:]...)
|
slf.sort = append(slf.sort[:index], slf.sort[index+1:]...)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +42,7 @@ func (slf *Deck[I]) GetCount() int {
|
||||||
|
|
||||||
// GetGroups 获取所有组
|
// GetGroups 获取所有组
|
||||||
func (slf *Deck[I]) GetGroups() map[int64]*Group[I] {
|
func (slf *Deck[I]) GetGroups() map[int64]*Group[I] {
|
||||||
return hash.Copy(slf.groups)
|
return collection.CloneMap(slf.groups)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGroupsSlice 获取所有组
|
// GetGroupsSlice 获取所有组
|
||||||
|
@ -57,7 +56,7 @@ func (slf *Deck[I]) GetGroupsSlice() []*Group[I] {
|
||||||
|
|
||||||
// GetNext 获取特定组的下一个组
|
// GetNext 获取特定组的下一个组
|
||||||
func (slf *Deck[I]) GetNext(guid int64) *Group[I] {
|
func (slf *Deck[I]) GetNext(guid int64) *Group[I] {
|
||||||
index := slice.GetIndex(slf.sort, guid)
|
index := collection.FindIndexInComparableSlice(slf.sort, guid)
|
||||||
if index == -1 {
|
if index == -1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -69,7 +68,7 @@ func (slf *Deck[I]) GetNext(guid int64) *Group[I] {
|
||||||
|
|
||||||
// GetPrev 获取特定组的上一个组
|
// GetPrev 获取特定组的上一个组
|
||||||
func (slf *Deck[I]) GetPrev(guid int64) *Group[I] {
|
func (slf *Deck[I]) GetPrev(guid int64) *Group[I] {
|
||||||
index := slice.GetIndex(slf.sort, guid)
|
index := collection.FindIndexInComparableSlice(slf.sort, guid)
|
||||||
if index == -1 {
|
if index == -1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -150,12 +150,12 @@ func Paths(dir string) []string {
|
||||||
return paths
|
return paths
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadLineWithParallelByChannel 并行的分行读取文件并行处理,处理过程中会将每一行的内容传入 handlerFunc 中进行处理
|
// ReadLineWithParallel 并行的分行读取文件并行处理,处理过程中会将每一行的内容传入 handlerFunc 中进行处理
|
||||||
// - 由于是并行处理,所以处理过程中的顺序是不确定的。
|
// - 由于是并行处理,所以处理过程中的顺序是不确定的。
|
||||||
// - 可通过 start 参数指定开始读取的位置,如果不指定则从文件开头开始读取。
|
// - 可通过 start 参数指定开始读取的位置,如果不指定则从文件开头开始读取。
|
||||||
func ReadLineWithParallel(filename string, chunkSize int64, handlerFunc func(string), start ...int64) (n int64, err error) {
|
func ReadLineWithParallel(filename string, chunkSize int64, handlerFunc func(string), start ...int64) (n int64, err error) {
|
||||||
file, err := os.Open(filename)
|
file, err := os.Open(filename)
|
||||||
offset := slice.GetValue(start, 0)
|
offset := collection.FindFirstOrDefaultInSlice(start, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return offset, err
|
return offset, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package fsm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewFSM 创建一个新的状态机
|
// NewFSM 创建一个新的状态机
|
||||||
|
@ -60,7 +60,7 @@ func (slf *FSM[State, Data]) Unregister(state State) {
|
||||||
|
|
||||||
// HasState 检查状态机是否存在特定状态
|
// HasState 检查状态机是否存在特定状态
|
||||||
func (slf *FSM[State, Data]) HasState(state State) bool {
|
func (slf *FSM[State, Data]) HasState(state State) bool {
|
||||||
return hash.Exist(slf.states, state)
|
return collection.FindInMapKey(slf.states, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change 改变状态机状态到新的状态
|
// Change 改变状态机状态到新的状态
|
||||||
|
|
|
@ -3,8 +3,8 @@ package geometry
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -317,7 +317,7 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
|
||||||
for i, directions := range [][]Direction{DirectionUDLR, DirectionLRUD} {
|
for i, directions := range [][]Direction{DirectionUDLR, DirectionLRUD} {
|
||||||
var direction Direction
|
var direction Direction
|
||||||
for {
|
for {
|
||||||
next, direction = slice.NextLoop(directions, next)
|
next, direction = collection.FindLoopedNextInSlice(directions, next)
|
||||||
for {
|
for {
|
||||||
directionPoint = GetDirectionNextWithPoint(direction, directionPoint)
|
directionPoint = GetDirectionNextWithPoint(direction, directionPoint)
|
||||||
if px, py := directionPoint.GetXY(); px < 0 || px >= areaWidth || py < 0 || py >= areaHeight {
|
if px, py := directionPoint.GetXY(); px < 0 || px >= areaWidth || py < 0 || py >= areaHeight {
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
package hash
|
|
||||||
|
|
||||||
// Chunk 将哈希表按照指定大小分块
|
|
||||||
// - m: 待分块的哈希表
|
|
||||||
// - size: 每块的大小
|
|
||||||
func Chunk[K comparable, V any](m map[K]V, size int) []map[K]V {
|
|
||||||
if len(m) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
i int
|
|
||||||
j int
|
|
||||||
)
|
|
||||||
chunks := make([]map[K]V, (len(m)-1)/size+1)
|
|
||||||
for i = 0; i < len(m); i += size {
|
|
||||||
chunks[j] = make(map[K]V, size)
|
|
||||||
for key, value := range m {
|
|
||||||
if i <= j*size && j*size < i+size {
|
|
||||||
chunks[j][key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
|
|
||||||
return chunks
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package hash
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"hash/crc32"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewConsistency(replicas int) *Consistency {
|
|
||||||
return &Consistency{
|
|
||||||
replicas: replicas,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consistency 一致性哈希生成
|
|
||||||
//
|
|
||||||
// https://blog.csdn.net/zhpCSDN921011/article/details/126845397
|
|
||||||
type Consistency struct {
|
|
||||||
replicas int // 虚拟节点的数量
|
|
||||||
keys []int // 所有虚拟节点的哈希值
|
|
||||||
hashMap map[int]int // 虚拟节点的哈希值: 节点(虚拟节点映射到真实节点)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNode 添加节点
|
|
||||||
func (slf *Consistency) AddNode(keys ...int) {
|
|
||||||
if slf.hashMap == nil {
|
|
||||||
slf.hashMap = map[int]int{}
|
|
||||||
}
|
|
||||||
if slf.replicas == 0 {
|
|
||||||
slf.replicas = 3
|
|
||||||
}
|
|
||||||
for _, key := range keys {
|
|
||||||
for i := 0; i < slf.replicas; i++ {
|
|
||||||
// 计算虚拟节点哈希值
|
|
||||||
hash := int(crc32.ChecksumIEEE([]byte(strconv.Itoa(i) + strconv.Itoa(key))))
|
|
||||||
|
|
||||||
// 存储虚拟节点哈希值
|
|
||||||
slf.keys = append(slf.keys, hash)
|
|
||||||
|
|
||||||
// 存入map做映射
|
|
||||||
slf.hashMap[hash] = key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//排序哈希值,下面匹配的时候要二分搜索
|
|
||||||
sort.Ints(slf.keys)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PickNode 获取与 key 最接近的节点
|
|
||||||
func (slf *Consistency) PickNode(key any) int {
|
|
||||||
if len(slf.keys) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
partitionKey := fmt.Sprintf("%#v", key)
|
|
||||||
beg := strings.Index(partitionKey, "{")
|
|
||||||
end := strings.Index(partitionKey, "}")
|
|
||||||
if beg != -1 && !(end == -1 || end == beg+1) {
|
|
||||||
partitionKey = partitionKey[beg+1 : end]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算传入key的哈希值
|
|
||||||
hash := int(crc32.ChecksumIEEE([]byte(partitionKey)))
|
|
||||||
|
|
||||||
// sort.Search使用二分查找满足m.Keys[i]>=hash的最小哈希值
|
|
||||||
idx := sort.Search(len(slf.keys), func(i int) bool { return slf.keys[i] >= hash })
|
|
||||||
|
|
||||||
// 若key的hash值大于最后一个虚拟节点的hash值,则选择第一个虚拟节点
|
|
||||||
if idx == len(slf.keys) {
|
|
||||||
idx = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return slf.hashMap[slf.keys[idx]]
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
package hash
|
|
||||||
|
|
||||||
// ToSlice 将 map 的 value 转换为切片
|
|
||||||
func ToSlice[K comparable, V any](m map[K]V) []V {
|
|
||||||
var s = make([]V, 0, len(m))
|
|
||||||
for _, v := range m {
|
|
||||||
s = append(s, v)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyToSlice 将 map 的 key 转换为切片
|
|
||||||
func KeyToSlice[K comparable, V any](m map[K]V) []K {
|
|
||||||
var s = make([]K, 0, len(m))
|
|
||||||
for k := range m {
|
|
||||||
s = append(s, k)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reversal 将 map 的 key 和 value 互换
|
|
||||||
func Reversal[K comparable, V comparable](m map[K]V) map[V]K {
|
|
||||||
var nm = make(map[V]K)
|
|
||||||
for k, v := range m {
|
|
||||||
nm[v] = k
|
|
||||||
}
|
|
||||||
return nm
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToMap 将切片转换为 map
|
|
||||||
func ToMap[V any](slice []V) map[int]V {
|
|
||||||
var m = make(map[int]V)
|
|
||||||
for i, v := range slice {
|
|
||||||
m[i] = v
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToIterator 将切片转换为 Iterator
|
|
||||||
func ToIterator[V comparable](slice []V) map[V]struct{} {
|
|
||||||
var m = make(map[V]struct{})
|
|
||||||
for _, v := range slice {
|
|
||||||
m[v] = struct{}{}
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToMapBool 将切片转换为 map,value作为Key
|
|
||||||
func ToMapBool[V comparable](slice []V) map[V]bool {
|
|
||||||
var m = make(map[V]bool)
|
|
||||||
for _, v := range slice {
|
|
||||||
m[v] = true
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSortMap 将切片转换为 SortMap
|
|
||||||
func ToSortMap[V any](slice []V) SortMap[int, V] {
|
|
||||||
var m SortMap[int, V]
|
|
||||||
for i, v := range slice {
|
|
||||||
m.Set(i, v)
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy 复制一个map
|
|
||||||
func Copy[K comparable, V any](m map[K]V) map[K]V {
|
|
||||||
var backup = make(map[K]V)
|
|
||||||
for k, v := range m {
|
|
||||||
backup[k] = v
|
|
||||||
}
|
|
||||||
return backup
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package hash
|
|
||||||
|
|
||||||
// RandomDrop 随机删除哈希表中的指定数量的元素
|
|
||||||
// - 该函数会修改原哈希表,如果不想修改原哈希表,请使用 RandomDropCopy
|
|
||||||
func RandomDrop[K comparable, V any](n int, hash map[K]V) map[K]V {
|
|
||||||
if n <= 0 || len(hash) <= n {
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
for k := range hash {
|
|
||||||
if n <= 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
delete(hash, k)
|
|
||||||
n--
|
|
||||||
}
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomDropCopy 随机删除哈希表中的指定数量的元素
|
|
||||||
// - 该函数不会修改原哈希表,如果想修改原哈希表,请使用 RandomDrop
|
|
||||||
func RandomDropCopy[K comparable, V any](n int, m map[K]V) map[K]V {
|
|
||||||
if n <= 0 || len(m) <= n {
|
|
||||||
return map[K]V{}
|
|
||||||
}
|
|
||||||
var nm = make(map[K]V, len(m))
|
|
||||||
for k, v := range m {
|
|
||||||
if n <= 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
nm[k] = v
|
|
||||||
n--
|
|
||||||
}
|
|
||||||
return nm
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropBy 从哈希表中删除指定的元素
|
|
||||||
func DropBy[K comparable, V any](m map[K]V, fn func(key K, value V) bool) map[K]V {
|
|
||||||
for k, v := range m {
|
|
||||||
if fn(k, v) {
|
|
||||||
delete(m, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropByCopy 与 DropBy 功能相同,但是该函数不会修改原哈希表
|
|
||||||
func DropByCopy[K comparable, V any](m map[K]V, fn func(key K, value V) bool) map[K]V {
|
|
||||||
var nm = make(map[K]V, len(m))
|
|
||||||
for k, v := range m {
|
|
||||||
if fn(k, v) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
nm[k] = v
|
|
||||||
}
|
|
||||||
return nm
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package hash
|
|
||||||
|
|
||||||
// Each 根据传入的 abort 遍历 m,如果 iterator 返回值与 abort 相同,则停止遍历
|
|
||||||
func Each[K comparable, V any](abort bool, m map[K]V, iterator func(i int, key K, item V) bool) {
|
|
||||||
i := 0
|
|
||||||
for k, v := range m {
|
|
||||||
if iterator(i, k, v) == abort {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachT 与 Each 的功能相同,但是 abort 被默认为 true
|
|
||||||
func EachT[K comparable, V any](m map[K]V, iterator func(i int, key K, item V) bool) {
|
|
||||||
Each(true, m, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachF 与 Each 的功能相同,但是 abort 被默认为 false
|
|
||||||
func EachF[K comparable, V any](m map[K]V, iterator func(i int, key K, item V) bool) {
|
|
||||||
Each(false, m, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachResult 根据传入的 abort 遍历 m,得到遍历的结果,如果 iterator 返回值中的 bool 值与 abort 相同,则停止遍历,并返回当前已积累的结果
|
|
||||||
func EachResult[K comparable, V any, R any](abort bool, m map[K]V, iterator func(i int, key K, item V) (R, bool)) []R {
|
|
||||||
var result []R
|
|
||||||
i := 0
|
|
||||||
for k, v := range m {
|
|
||||||
r, ok := iterator(i, k, v)
|
|
||||||
result = append(result, r)
|
|
||||||
if ok == abort {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachResultT 与 EachResult 的功能相同,但是 abort 被默认为 true
|
|
||||||
func EachResultT[K comparable, V any, R any](m map[K]V, iterator func(i int, key K, item V) (R, bool)) []R {
|
|
||||||
return EachResult(true, m, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachResultF 与 EachResult 的功能相同,但是 abort 被默认为 false
|
|
||||||
func EachResultF[K comparable, V any, R any](m map[K]V, iterator func(i int, key K, item V) (R, bool)) []R {
|
|
||||||
return EachResult(false, m, iterator)
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package hash
|
|
||||||
|
|
||||||
// Filter 根据特定的表达式过滤哈希表成员
|
|
||||||
// - reserve: 是否保留符合条件的成员
|
|
||||||
// - m: 待过滤的哈希表
|
|
||||||
// - expression: 过滤表达式
|
|
||||||
//
|
|
||||||
// 这个函数的作用是遍历输入哈希表 m,然后根据 expression 函数的返回值来决定是否保留每个元素。具体来说
|
|
||||||
// - 如果 expression 返回 true 并且 reserve 也是 true,那么元素会被保留
|
|
||||||
// - 如果 expression 返回 false 并且 reserve 是 false,那么元素也会被保留
|
|
||||||
//
|
|
||||||
// 该没有创建新的内存空间或进行元素复制,所以整个操作相当高效。同时,由于 m 和 map 实际上共享底层的数组,因此这个函数会改变传入的 map。如果不希望改变原哈希表,需要在函数调用之前手动复制一份或者使用 FilterCopy 函数。
|
|
||||||
func Filter[K comparable, V any](reserve bool, m map[K]V, expression func(key K, value V) bool) map[K]V {
|
|
||||||
if len(m) == 0 {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range m {
|
|
||||||
if !expression(key, value) {
|
|
||||||
delete(m, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterT 与 Filter 的功能相同,但是 reserve 被默认为 true
|
|
||||||
func FilterT[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V {
|
|
||||||
return Filter(true, m, expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterF 与 Filter 的功能相同,但是 reserve 被默认为 false
|
|
||||||
func FilterF[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V {
|
|
||||||
return Filter(false, m, expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterCopy 与 Filter 的功能相同,但是不会改变原哈希表,而是返回一个新的哈希表
|
|
||||||
func FilterCopy[K comparable, V any](reserve bool, m map[K]V, expression func(key K, value V) bool) map[K]V {
|
|
||||||
var res = map[K]V{}
|
|
||||||
for key, value := range m {
|
|
||||||
if expression(key, value) {
|
|
||||||
res[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterTCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 true
|
|
||||||
func FilterTCopy[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V {
|
|
||||||
return FilterCopy(true, m, expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterFCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 false
|
|
||||||
func FilterFCopy[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V {
|
|
||||||
return FilterCopy(false, m, expression)
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package hash
|
|
||||||
|
|
||||||
import "encoding/json"
|
|
||||||
|
|
||||||
// Exist 检查特定 key 是否存在
|
|
||||||
func Exist[K comparable, V any](m map[K]V, key K) bool {
|
|
||||||
_, exist := m[key]
|
|
||||||
return exist
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllExist 检查多个 key 是否存在
|
|
||||||
func AllExist[K comparable, V any](m map[K]V, keys ...K) bool {
|
|
||||||
for key := range m {
|
|
||||||
if _, exist := m[key]; !exist {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToJson 将 map 转换为 json 字符串
|
|
||||||
func ToJson[K comparable, V any](m map[K]V) string {
|
|
||||||
if data, err := json.Marshal(m); err == nil {
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
return "{}"
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomGet 随机获取一个元素
|
|
||||||
func RandomGet[K comparable, V any](m map[K]V) (v V) {
|
|
||||||
for _, v := range m {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomGetKey 随机获取一个 key
|
|
||||||
func RandomGetKey[K comparable, V any](m map[K]V) (k K) {
|
|
||||||
for k = range m {
|
|
||||||
return k
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomGetN 随机获取 n 个元素
|
|
||||||
// - 获取到的元素不会是重复的,当 map 的长度不足 n 时,返回的元素等同于 hash.ToSlice
|
|
||||||
func RandomGetN[K comparable, V any](m map[K]V, n int) (vs []V) {
|
|
||||||
for _, v := range m {
|
|
||||||
vs = append(vs, v)
|
|
||||||
if len(vs) >= n {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomGetKeyN 随机获取 n 个 key
|
|
||||||
// - 获取到的元素不会是重复的,当 map 的长度不足 n 时,返回的元素等同于 hash.KeyToSlice
|
|
||||||
func RandomGetKeyN[K comparable, V any](m map[K]V, n int) (ks []K) {
|
|
||||||
for k := range m {
|
|
||||||
ks = append(ks, k)
|
|
||||||
if len(ks) >= n {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear 清空 map
|
|
||||||
func Clear[K comparable, V any](m map[K]V) {
|
|
||||||
for k := range m {
|
|
||||||
delete(m, k)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package hash
|
|
||||||
|
|
||||||
import "github.com/kercylan98/minotaur/utils/generic"
|
|
||||||
|
|
||||||
// Sum 计算一个 map 中的 value 总和
|
|
||||||
func Sum[K comparable, V generic.Number](m map[K]V) V {
|
|
||||||
var sum V
|
|
||||||
for _, v := range m {
|
|
||||||
sum += v
|
|
||||||
}
|
|
||||||
return sum
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
package hash
|
|
||||||
|
|
||||||
// NewSet 创建一个 Set 集合
|
|
||||||
func NewSet[K comparable](ks ...K) Set[K] {
|
|
||||||
s := make(Set[K])
|
|
||||||
s.AddAll(ks...)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set 集合
|
|
||||||
type Set[K comparable] map[K]struct{}
|
|
||||||
|
|
||||||
// Exist 检查特定 key 是否存在
|
|
||||||
func (s Set[K]) Exist(key K) bool {
|
|
||||||
_, exist := s[key]
|
|
||||||
return exist
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllExist 检查多个 key 是否存在
|
|
||||||
func (s Set[K]) AllExist(keys ...K) bool {
|
|
||||||
for _, key := range keys {
|
|
||||||
if _, exist := s[key]; !exist {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add 添加元素
|
|
||||||
func (s Set[K]) Add(key K) {
|
|
||||||
s[key] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddAll 添加多个元素
|
|
||||||
func (s Set[K]) AddAll(keys ...K) {
|
|
||||||
for _, key := range keys {
|
|
||||||
s[key] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove 移除元素
|
|
||||||
func (s Set[K]) Remove(key K) {
|
|
||||||
delete(s, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveAll 移除多个元素
|
|
||||||
func (s Set[K]) RemoveAll(keys ...K) {
|
|
||||||
for _, key := range keys {
|
|
||||||
delete(s, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear 清空集合
|
|
||||||
func (s Set[K]) Clear() {
|
|
||||||
for key := range s {
|
|
||||||
delete(s, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size 集合长度
|
|
||||||
func (s Set[K]) Size() int {
|
|
||||||
return len(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSlice 转换为切片
|
|
||||||
func (s Set[K]) ToSlice() []K {
|
|
||||||
keys := make([]K, 0, len(s))
|
|
||||||
for key := range s {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToMap 转换为 map
|
|
||||||
func (s Set[K]) ToMap() map[K]struct{} {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToJson 转换为 json 字符串
|
|
||||||
func (s Set[K]) ToJson() string {
|
|
||||||
return ToJson(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomGet 随机获取一个元素
|
|
||||||
func (s Set[K]) RandomGet() (k K) {
|
|
||||||
for k = range s {
|
|
||||||
return k
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
package hash
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewSortMap[K comparable, V any]() *SortMap[K, V] {
|
|
||||||
return &SortMap[K, V]{
|
|
||||||
m: map[K]V{},
|
|
||||||
s: map[int]K{},
|
|
||||||
r: map[K]int{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SortMap 有序的 map 实现
|
|
||||||
type SortMap[K comparable, V any] struct {
|
|
||||||
i int
|
|
||||||
m map[K]V
|
|
||||||
s map[int]K
|
|
||||||
r map[K]int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *SortMap[K, V]) Set(key K, value V) {
|
|
||||||
if i, exist := slf.r[key]; exist {
|
|
||||||
slf.s[i] = key
|
|
||||||
slf.m[key] = value
|
|
||||||
} else {
|
|
||||||
slf.m[key] = value
|
|
||||||
slf.s[slf.i] = key
|
|
||||||
slf.r[key] = slf.i
|
|
||||||
slf.i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *SortMap[K, V]) Del(key K) {
|
|
||||||
if _, exist := slf.m[key]; exist {
|
|
||||||
delete(slf.s, slf.r[key])
|
|
||||||
delete(slf.r, key)
|
|
||||||
delete(slf.m, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *SortMap[K, V]) Get(key K) V {
|
|
||||||
v := slf.m[key]
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *SortMap[K, V]) For(handle func(key K, value V) bool) {
|
|
||||||
for k, v := range slf.m {
|
|
||||||
if !handle(k, v) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *SortMap[K, V]) ForSort(handle func(key K, value V) bool) {
|
|
||||||
var indexes []int
|
|
||||||
for i := range slf.s {
|
|
||||||
indexes = append(indexes, i)
|
|
||||||
}
|
|
||||||
sort.Ints(indexes)
|
|
||||||
for _, i := range indexes {
|
|
||||||
k := slf.s[i]
|
|
||||||
if !handle(k, slf.m[k]) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *SortMap[K, V]) ToMap() map[K]V {
|
|
||||||
var m = make(map[K]V)
|
|
||||||
for k, v := range slf.m {
|
|
||||||
m[k] = v
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *SortMap[K, V]) ToSlice() []V {
|
|
||||||
var s = make([]V, 0, len(slf.m))
|
|
||||||
for _, v := range slf.m {
|
|
||||||
s = append(s, v)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *SortMap[K, V]) ToSliceSort() []V {
|
|
||||||
var indexes []int
|
|
||||||
for i := range slf.s {
|
|
||||||
indexes = append(indexes, i)
|
|
||||||
}
|
|
||||||
sort.Ints(indexes)
|
|
||||||
var result []V
|
|
||||||
for _, i := range indexes {
|
|
||||||
k := slf.s[i]
|
|
||||||
result = append(result, slf.m[k])
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *SortMap[K, V]) KeyToSlice() []K {
|
|
||||||
var s = make([]K, 0, len(slf.m))
|
|
||||||
for k := range slf.m {
|
|
||||||
s = append(s, k)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
package maths
|
package maths
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ func Compare[V generic.Ordered](a V, expression CompareExpression, b V) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsContinuity 检查一组值是否连续
|
// IsContinuity 检查一组值是否连续
|
||||||
func IsContinuity[V generic.Integer](values []V) bool {
|
func IsContinuity[S ~[]V, V generic.Integer](values S) bool {
|
||||||
length := len(values)
|
length := len(values)
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
return false
|
return false
|
||||||
|
@ -52,10 +52,10 @@ func IsContinuity[V generic.Integer](values []V) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsContinuityWithSort 检查一组值排序后是否连续
|
// IsContinuityWithSort 检查一组值排序后是否连续
|
||||||
func IsContinuityWithSort[V generic.Integer](values []V) bool {
|
func IsContinuityWithSort[S ~[]V, V generic.Integer](values S) bool {
|
||||||
sli := slice.Copy(values)
|
s := collection.CloneSlice(values)
|
||||||
sort.Slice(sli, func(i, j int) bool {
|
sort.Slice(s, func(i, j int) bool {
|
||||||
return sli[i] < sli[j]
|
return s[i] < s[j]
|
||||||
})
|
})
|
||||||
return IsContinuity(sli)
|
return IsContinuity(s)
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,7 @@ func UnMergeInt64[V generic.SignedNumber](n int64) (V, V) {
|
||||||
|
|
||||||
// ToContinuous 将一组非连续的数字转换为从1开始的连续数字
|
// ToContinuous 将一组非连续的数字转换为从1开始的连续数字
|
||||||
// - 返回值是一个 map,key 是从 1 开始的连续数字,value 是原始数字
|
// - 返回值是一个 map,key 是从 1 开始的连续数字,value 是原始数字
|
||||||
func ToContinuous[V generic.Integer](nums []V) map[V]V {
|
func ToContinuous[S ~[]V, V generic.Integer](nums S) map[V]V {
|
||||||
if len(nums) == 0 {
|
if len(nums) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package memory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/collection"
|
||||||
"github.com/kercylan98/minotaur/utils/random"
|
"github.com/kercylan98/minotaur/utils/random"
|
||||||
"github.com/kercylan98/minotaur/utils/super"
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -81,7 +81,7 @@ func BindPersistCacheProgram[OutputParamHandlerFunc any](name string, handler Ou
|
||||||
cachesRWMutex.RUnlock()
|
cachesRWMutex.RUnlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
funcCache = hash.Copy(funcCache)
|
funcCache = collection.CloneMap(funcCache)
|
||||||
cachesRWMutex.RUnlock()
|
cachesRWMutex.RUnlock()
|
||||||
for _, results := range funcCache {
|
for _, results := range funcCache {
|
||||||
persist.Call(results)
|
persist.Call(results)
|
||||||
|
@ -111,7 +111,7 @@ func BindPersistCacheProgram[OutputParamHandlerFunc any](name string, handler Ou
|
||||||
cachesRWMutex.RUnlock()
|
cachesRWMutex.RUnlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
funcCache = hash.Copy(funcCache)
|
funcCache = collection.CloneMap(funcCache)
|
||||||
cachesRWMutex.RUnlock()
|
cachesRWMutex.RUnlock()
|
||||||
delay := opt.delay
|
delay := opt.delay
|
||||||
tick := delay
|
tick := delay
|
||||||
|
|
|
@ -1,308 +0,0 @@
|
||||||
package sher
|
|
||||||
|
|
||||||
import "sort"
|
|
||||||
|
|
||||||
type ComparisonHandler[V any] func(source, target V) bool
|
|
||||||
|
|
||||||
// InSlice 判断切片中是否包含某个元素
|
|
||||||
func InSlice[S ~[]V, V any](slice S, v V, handler ComparisonHandler[V]) bool {
|
|
||||||
if slice == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, value := range slice {
|
|
||||||
if handler(v, value) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// InSliceByBinarySearch 判断切片中是否包含某个元素,使用二分搜索
|
|
||||||
func InSliceByBinarySearch[S ~[]V, V any](slice S, v V, handler ComparisonHandler[V]) bool {
|
|
||||||
return sort.Search(len(slice), func(i int) bool {
|
|
||||||
return handler(v, slice[i])
|
|
||||||
}) != len(slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllInSlice 判断切片中是否包含所有元素
|
|
||||||
func AllInSlice[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
if slice == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, value := range values {
|
|
||||||
if !InSlice(slice, value, handler) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllInSliceByBinarySearch 判断切片中是否包含所有元素,使用二分搜索
|
|
||||||
func AllInSliceByBinarySearch[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
if slice == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, value := range values {
|
|
||||||
if !InSliceByBinarySearch(slice, value, handler) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyInSlice 判断切片中是否包含任意一个元素
|
|
||||||
func AnyInSlice[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
if slice == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, value := range values {
|
|
||||||
if InSlice(slice, value, handler) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyInSliceByBinarySearch 判断切片中是否包含任意一个元素,使用二分搜索
|
|
||||||
func AnyInSliceByBinarySearch[S ~[]V, V any](slice S, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
if slice == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, value := range values {
|
|
||||||
if InSliceByBinarySearch(slice, value, handler) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// InSlices 判断多个切片中是否包含某个元素
|
|
||||||
func InSlices[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
|
||||||
return InSlice(MergeSlices(slices...), v, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InSlicesByBinarySearch 判断多个切片中是否包含某个元素,使用二分搜索
|
|
||||||
func InSlicesByBinarySearch[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
|
||||||
return InSliceByBinarySearch(MergeSlices(slices...), v, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllInSlices 判断多个切片中是否包含所有元素
|
|
||||||
func AllInSlices[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
return AllInSlice(MergeSlices(slices...), values, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllInSlicesByBinarySearch 判断多个切片中是否包含所有元素,使用二分搜索
|
|
||||||
func AllInSlicesByBinarySearch[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
return AllInSliceByBinarySearch(MergeSlices(slices...), values, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyInSlices 判断多个切片中是否包含任意一个元素
|
|
||||||
func AnyInSlices[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
return AnyInSlice(MergeSlices(slices...), values, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyInSlicesByBinarySearch 判断多个切片中是否包含任意一个元素,使用二分搜索
|
|
||||||
func AnyInSlicesByBinarySearch[S ~[]V, V any](slices []S, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
return AnyInSliceByBinarySearch(MergeSlices(slices...), values, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InAllSlices 判断元素是否在所有切片中都存在
|
|
||||||
func InAllSlices[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
|
||||||
if slices == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, slice := range slices {
|
|
||||||
if !InSlice(slice, v, handler) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// InAllSlicesByBinarySearch 判断元素是否在所有切片中都存在,使用二分搜索
|
|
||||||
func InAllSlicesByBinarySearch[S ~[]V, V any](slices []S, v V, handler ComparisonHandler[V]) bool {
|
|
||||||
if slices == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, slice := range slices {
|
|
||||||
if !InSliceByBinarySearch(slice, v, handler) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyInAllSlices 判断元素是否在所有切片中都存在任意至少一个
|
|
||||||
func AnyInAllSlices[S ~[]V, V any](slices []S, v []V, handler ComparisonHandler[V]) bool {
|
|
||||||
if slices == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, slice := range slices {
|
|
||||||
if AnyInSlice(slice, v, handler) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyInAllSlicesByBinarySearch 判断元素是否在所有切片中都存在任意至少一个,使用二分搜索
|
|
||||||
func AnyInAllSlicesByBinarySearch[S ~[]V, V any](slices []S, v []V, handler ComparisonHandler[V]) bool {
|
|
||||||
if slices == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, slice := range slices {
|
|
||||||
if AnyInSliceByBinarySearch(slice, v, handler) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyInMap 判断 map 中是否包含某个 key
|
|
||||||
func KeyInMap[M ~map[K]V, K comparable, V any](m M, key K) bool {
|
|
||||||
_, ok := m[key]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValueInMap 判断 map 中是否包含某个 value
|
|
||||||
func ValueInMap[M ~map[K]V, K comparable, V any](m M, value V, handler ComparisonHandler[V]) bool {
|
|
||||||
if m == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, v := range m {
|
|
||||||
if handler(value, v) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllKeyInMap 判断 map 中是否包含所有 key
|
|
||||||
func AllKeyInMap[M ~map[K]V, K comparable, V any](m M, keys ...K) bool {
|
|
||||||
if m == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, key := range keys {
|
|
||||||
if !KeyInMap(m, key) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllValueInMap 判断 map 中是否包含所有 value
|
|
||||||
func AllValueInMap[M ~map[K]V, K comparable, V any](m M, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
if m == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, value := range values {
|
|
||||||
if !ValueInMap(m, value, handler) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyKeyInMap 判断 map 中是否包含任意一个 key
|
|
||||||
func AnyKeyInMap[M ~map[K]V, K comparable, V any](m M, keys ...K) bool {
|
|
||||||
if m == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, key := range keys {
|
|
||||||
if KeyInMap(m, key) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyValueInMap 判断 map 中是否包含任意一个 value
|
|
||||||
func AnyValueInMap[M ~map[K]V, K comparable, V any](m M, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
if m == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, value := range values {
|
|
||||||
if ValueInMap(m, value, handler) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllKeyInMaps 判断多个 map 中是否包含所有 key
|
|
||||||
func AllKeyInMaps[M ~map[K]V, K comparable, V any](maps []M, keys ...K) bool {
|
|
||||||
if maps == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, m := range maps {
|
|
||||||
if !AllKeyInMap(m, keys...) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllValueInMaps 判断多个 map 中是否包含所有 value
|
|
||||||
func AllValueInMaps[M ~map[K]V, K comparable, V any](maps []M, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
if maps == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, m := range maps {
|
|
||||||
if !AllValueInMap(m, values, handler) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyKeyInMaps 判断多个 map 中是否包含任意一个 key
|
|
||||||
func AnyKeyInMaps[M ~map[K]V, K comparable, V any](maps []M, keys ...K) bool {
|
|
||||||
if maps == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, m := range maps {
|
|
||||||
if AnyKeyInMap(m, keys...) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyValueInMaps 判断多个 map 中是否包含任意一个 value
|
|
||||||
func AnyValueInMaps[M ~map[K]V, K comparable, V any](maps []M, values []V, handler ComparisonHandler[V]) bool {
|
|
||||||
if maps == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, m := range maps {
|
|
||||||
if AnyValueInMap(m, values, handler) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// InAllMaps 判断元素是否在所有 map 中都存在
|
|
||||||
func InAllMaps[M ~map[K]V, K comparable, V any](maps []M, key K) bool {
|
|
||||||
if maps == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, m := range maps {
|
|
||||||
if !KeyInMap(m, key) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnyInAllMaps 判断元素是否在所有 map 中都存在任意至少一个
|
|
||||||
func AnyInAllMaps[M ~map[K]V, K comparable, V any](maps []M, keys []K) bool {
|
|
||||||
if maps == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, m := range maps {
|
|
||||||
if AnyKeyInMap(m, keys...) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
// Package sher 用于对 slice 和 map 操作的工具函数,将逐步替代 utils/slice 和 utils/map
|
|
||||||
package sher
|
|
|
@ -1,118 +0,0 @@
|
||||||
package sher_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kercylan98/minotaur/utils/sher"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDeduplicateSliceInPlace(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
s []int
|
|
||||||
expected []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
sher.DeduplicateSliceInPlace(&c.s)
|
|
||||||
if len(c.s) != len(c.expected) {
|
|
||||||
t.Errorf("DeduplicateSliceInPlace(%v) == %v, expected %v", c.s, c.s, c.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeduplicateSlice(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
s []int
|
|
||||||
expected []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
sl := len(c.s)
|
|
||||||
if r := sher.DeduplicateSlice(c.s); len(r) != len(c.expected) || len(c.s) != sl {
|
|
||||||
t.Errorf("DeduplicateSlice(%v) == %v, expected %v", c.s, r, c.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeduplicateSliceInPlaceWithCompare(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
s []int
|
|
||||||
expected []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
sher.DeduplicateSliceInPlaceWithCompare(&c.s, func(a, b int) bool {
|
|
||||||
return a == b
|
|
||||||
})
|
|
||||||
if len(c.s) != len(c.expected) {
|
|
||||||
t.Errorf("DeduplicateSliceInPlaceWithCompare(%v) == %v, expected %v", c.s, c.s, c.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeduplicateSliceWithCompare(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
s []int
|
|
||||||
expected []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
s: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2},
|
|
||||||
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
sl := len(c.s)
|
|
||||||
if r := sher.DeduplicateSliceWithCompare(c.s, func(a, b int) bool {
|
|
||||||
return a == b
|
|
||||||
}); len(r) != len(c.expected) || len(c.s) != sl {
|
|
||||||
t.Errorf("DeduplicateSliceWithCompare(%v) == %v, expected %v", c.s, r, c.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
package sher
|
|
||||||
|
|
||||||
import "github.com/kercylan98/minotaur/utils/generic"
|
|
||||||
|
|
||||||
// FindInSlice 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1
|
|
||||||
func FindInSlice[S ~[]V, V any](slice S, handler func(v V) bool) (i int, t V) {
|
|
||||||
if slice == nil {
|
|
||||||
return -1, t
|
|
||||||
}
|
|
||||||
for i, v := range slice {
|
|
||||||
if handler(v) {
|
|
||||||
return i, v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, t
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindInSliceByBinary 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1
|
|
||||||
func FindInSliceByBinary[S ~[]V, V any](slice S, handler func(v V) bool) (i int, t V) {
|
|
||||||
low := 0
|
|
||||||
high := len(slice) - 1
|
|
||||||
|
|
||||||
for low <= high {
|
|
||||||
mid := low + (high-low)/2
|
|
||||||
if handler(slice[mid]) {
|
|
||||||
return mid, slice[mid]
|
|
||||||
} else if handler(slice[mid]) {
|
|
||||||
high = mid - 1
|
|
||||||
} else {
|
|
||||||
low = mid + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1, t
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindMinimumInSlice 获取切片中的最小值
|
|
||||||
func FindMinimumInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (result V) {
|
|
||||||
if slice == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result = slice[0]
|
|
||||||
for i := 1; i < len(slice); i++ {
|
|
||||||
if handler(slice[i], result) {
|
|
||||||
result = slice[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindMaximumInSlice 获取切片中的最大值
|
|
||||||
func FindMaximumInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (result V) {
|
|
||||||
if slice == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result = slice[0]
|
|
||||||
for i := 1; i < len(slice); i++ {
|
|
||||||
if handler(result, slice[i]) {
|
|
||||||
result = slice[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindMin2MaxInSlice 获取切片中的最小值和最大值
|
|
||||||
func FindMin2MaxInSlice[S ~[]V, V generic.Number](slice S, handler ComparisonHandler[V]) (min, max V) {
|
|
||||||
if slice == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
min = slice[0]
|
|
||||||
max = slice[0]
|
|
||||||
for i := 1; i < len(slice); i++ {
|
|
||||||
if handler(slice[i], min) {
|
|
||||||
min = slice[i]
|
|
||||||
}
|
|
||||||
if handler(max, slice[i]) {
|
|
||||||
max = slice[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindMinFromMap 获取 map 中的最小值
|
|
||||||
func FindMinFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (result V) {
|
|
||||||
if m == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, v := range m {
|
|
||||||
if handler(v, result) {
|
|
||||||
result = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindMaxFromMap 获取 map 中的最大值
|
|
||||||
func FindMaxFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (result V) {
|
|
||||||
if m == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, v := range m {
|
|
||||||
if handler(result, v) {
|
|
||||||
result = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindMin2MaxFromMap 获取 map 中的最小值和最大值
|
|
||||||
func FindMin2MaxFromMap[M ~map[K]V, K comparable, V generic.Number](m M, handler ComparisonHandler[V]) (min, max V) {
|
|
||||||
if m == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, v := range m {
|
|
||||||
if handler(v, min) {
|
|
||||||
min = v
|
|
||||||
}
|
|
||||||
if handler(max, v) {
|
|
||||||
max = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
// Chunk 返回分块后的切片
|
|
||||||
func Chunk[T any](collection []T, size int) [][]T {
|
|
||||||
if len(collection) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if size < 1 {
|
|
||||||
panic("size must be greater than 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make([][]T, 0, (len(collection)+size-1)/size)
|
|
||||||
for size < len(collection) {
|
|
||||||
collection, result = collection[size:], append(result, collection[0:size])
|
|
||||||
}
|
|
||||||
return append(result, collection)
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package slice_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestChunk(t *testing.T) {
|
|
||||||
var collection = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
|
||||||
var chunks = slice.Chunk(collection, 3)
|
|
||||||
for _, chunk := range chunks {
|
|
||||||
t.Log(chunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleChunk() {
|
|
||||||
var collection = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
|
||||||
var chunks = slice.Chunk(collection, 3)
|
|
||||||
for _, chunk := range chunks {
|
|
||||||
fmt.Println(chunk)
|
|
||||||
}
|
|
||||||
// Output:
|
|
||||||
// [1 2 3]
|
|
||||||
// [4 5 6]
|
|
||||||
// [7 8 9]
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkChunk(b *testing.B) {
|
|
||||||
var collection = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
slice.Chunk(collection, 3)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
// Drop 从切片中删除指定数量的元素
|
|
||||||
// - start: 起始位置
|
|
||||||
// - n: 删除的元素数量
|
|
||||||
// - slice: 待删除元素的切片
|
|
||||||
//
|
|
||||||
// 关于 start 的取值:
|
|
||||||
// - 当 start < 0 时,start 将会从右至左计数,即 -1 表示最后一个元素,-2 表示倒数第二个元素,以此类推
|
|
||||||
func Drop[V any](start, n int, slice []V) []V {
|
|
||||||
var s = make([]V, len(slice))
|
|
||||||
copy(s, slice)
|
|
||||||
if start < 0 {
|
|
||||||
start = len(s) + start - n + 1
|
|
||||||
if start < 0 {
|
|
||||||
start = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
end := start + n
|
|
||||||
if end > len(s) {
|
|
||||||
end = len(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(s[:start], s[end:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropBy 从切片中删除指定的元素
|
|
||||||
// - slice: 待删除元素的切片
|
|
||||||
func DropBy[V any](slice []V, fn func(index int, value V) bool) []V {
|
|
||||||
var s = make([]V, len(slice))
|
|
||||||
copy(s, slice)
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
if fn(i, s[i]) {
|
|
||||||
s = append(s[:i], s[i+1:]...)
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package slice_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDrop(t *testing.T) {
|
|
||||||
s := []int{1, 2, 3, 4, 5}
|
|
||||||
t.Log(s, slice.Drop(1, 3, s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleDrop() {
|
|
||||||
fmt.Println(slice.Drop(1, 3, []int{1, 2, 3, 4, 5}))
|
|
||||||
// Output:
|
|
||||||
// [1 5]
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkDrop(b *testing.B) {
|
|
||||||
s := []int{1, 2, 3, 4, 5}
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
slice.Drop(1, 3, s)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
// Each 根据传入的 abort 遍历 slice,如果 iterator 返回值与 abort 相同,则停止遍历
|
|
||||||
func Each[V any](abort bool, slice []V, iterator func(index int, item V) bool) {
|
|
||||||
for i := 0; i < len(slice); i++ {
|
|
||||||
if iterator(i, slice[i]) == abort {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachT 与 Each 的功能相同,但是 abort 被默认为 true
|
|
||||||
func EachT[V any](slice []V, iterator func(index int, item V) bool) {
|
|
||||||
Each(true, slice, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachF 与 Each 的功能相同,但是 abort 被默认为 false
|
|
||||||
func EachF[V any](slice []V, iterator func(index int, item V) bool) {
|
|
||||||
Each(false, slice, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachReverse 根据传入的 abort 从后往前遍历 slice,如果 iterator 返回值与 abort 相同,则停止遍历
|
|
||||||
func EachReverse[V any](abort bool, slice []V, iterator func(index int, item V) bool) {
|
|
||||||
for i := len(slice) - 1; i >= 0; i-- {
|
|
||||||
if iterator(i, slice[i]) == abort {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachReverseT 与 EachReverse 的功能相同,但是 abort 被默认为 true
|
|
||||||
func EachReverseT[V any](slice []V, iterator func(index int, item V) bool) {
|
|
||||||
EachReverse(true, slice, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachReverseF 与 EachReverse 的功能相同,但是 abort 被默认为 false
|
|
||||||
func EachReverseF[V any](slice []V, iterator func(index int, item V) bool) {
|
|
||||||
EachReverse(false, slice, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachResult 根据传入的 abort 遍历 slice,得到遍历的结果,如果 iterator 返回值中的 bool 值与 abort 相同,则停止遍历,并返回当前已积累的结果
|
|
||||||
func EachResult[V any, R any](abort bool, slice []V, iterator func(index int, item V) (R, bool)) []R {
|
|
||||||
var result []R
|
|
||||||
for i := 0; i < len(slice); i++ {
|
|
||||||
r, ok := iterator(i, slice[i])
|
|
||||||
result = append(result, r)
|
|
||||||
if ok == abort {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachResultT 与 EachResult 的功能相同,但是 abort 被默认为 true
|
|
||||||
func EachResultT[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R {
|
|
||||||
return EachResult(true, slice, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachResultF 与 EachResult 的功能相同,但是 abort 被默认为 false
|
|
||||||
func EachResultF[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R {
|
|
||||||
return EachResult(false, slice, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachResultReverse 根据传入的 abort 从后往前遍历 slice,得到遍历的结果,如果 iterator 返回值中的 bool 值与 abort 相同,则停止遍历,并返回当前已积累的结果
|
|
||||||
func EachResultReverse[V any, R any](abort bool, slice []V, iterator func(index int, item V) (R, bool)) []R {
|
|
||||||
var result []R
|
|
||||||
for i := len(slice) - 1; i >= 0; i-- {
|
|
||||||
r, ok := iterator(i, slice[i])
|
|
||||||
result = append(result, r)
|
|
||||||
if ok == abort {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachResultReverseT 与 EachResultReverse 的功能相同,但是 abort 被默认为 true
|
|
||||||
func EachResultReverseT[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R {
|
|
||||||
return EachResultReverse(true, slice, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachResultReverseF 与 EachResultReverse 的功能相同,但是 abort 被默认为 false
|
|
||||||
func EachResultReverseF[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R {
|
|
||||||
return EachResultReverse(false, slice, iterator)
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
// FillBy 用指定的值填充切片
|
|
||||||
// - slice: 待填充的切片
|
|
||||||
func FillBy[V any](slice []V, fn func(index int, value V) V) []V {
|
|
||||||
for i, v := range slice {
|
|
||||||
slice[i] = fn(i, v)
|
|
||||||
}
|
|
||||||
return slice
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillByCopy 与 FillBy 的功能相同,但是不会改变原切片,而是返回一个新的切片
|
|
||||||
func FillByCopy[V any](slice []V, fn func(index int, value V) V) []V {
|
|
||||||
var s = make([]V, len(slice))
|
|
||||||
copy(s, slice)
|
|
||||||
return FillBy(s, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillUntil 用指定的值填充切片,如果 fn 返回的 bool 值与 abort 相同,则停止填充
|
|
||||||
// - abort: 填充中止条件
|
|
||||||
// - slice: 待填充的切片
|
|
||||||
func FillUntil[V any](abort bool, slice []V, fn func(index int, value V) (V, bool)) []V {
|
|
||||||
for i, v := range slice {
|
|
||||||
if value, b := fn(i, v); b == abort {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
slice[i] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return slice
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillUntilCopy 与 FillUntil 的功能相同,但不会改变原切片,而是返回一个新的切片
|
|
||||||
func FillUntilCopy[V any](abort bool, slice []V, fn func(index int, value V) (V, bool)) []V {
|
|
||||||
var s = make([]V, len(slice))
|
|
||||||
copy(s, slice)
|
|
||||||
return FillUntil(abort, s, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillUntilT 是 FillUntil 的简化版本,其中 abort 参数为 true
|
|
||||||
func FillUntilT[V any](slice []V, fn func(index int, value V) (V, bool)) []V {
|
|
||||||
return FillUntil(true, slice, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillUntilF 是 FillUntil 的简化版本,其中 abort 参数为 false
|
|
||||||
func FillUntilF[V any](slice []V, fn func(index int, value V) (V, bool)) []V {
|
|
||||||
return FillUntil(false, slice, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillUntilTCopy 是 FillUntilCopy 的简化版本,其中 abort 参数为 true
|
|
||||||
func FillUntilTCopy[V any](slice []V, fn func(index int, value V) (V, bool)) []V {
|
|
||||||
return FillUntilCopy(true, slice, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillUntilFCopy 是 FillUntilCopy 的简化版本,其中 abort 参数为 false
|
|
||||||
func FillUntilFCopy[V any](slice []V, fn func(index int, value V) (V, bool)) []V {
|
|
||||||
return FillUntilCopy(false, slice, fn)
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
// Filter 根据特定的表达式过滤切片成员
|
|
||||||
// - reserve: 是否保留符合条件的成员
|
|
||||||
// - slice: 待过滤的切片
|
|
||||||
// - expression: 过滤表达式
|
|
||||||
//
|
|
||||||
// 这个函数的作用是遍历输入切片 slice,然后根据 expression 函数的返回值来决定是否保留每个元素。具体来说
|
|
||||||
// - 如果 expression 返回 true 并且 reserve 也是 true,那么元素会被保留
|
|
||||||
// - 如果 expression 返回 false 并且 reserve 是 false,那么元素也会被保留
|
|
||||||
//
|
|
||||||
// 该没有创建新的内存空间或进行元素复制,所以整个操作相当高效。同时,由于 s 和 slice 实际上共享底层的数组,因此这个函数会改变传入的 slice。如果不希望改变原切片,需要在函数调用之前手动复制一份或者使用 FilterCopy 函数。
|
|
||||||
func Filter[V any](reserve bool, slice []V, expression func(index int, item V) bool) []V {
|
|
||||||
if len(slice) == 0 {
|
|
||||||
return slice
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
i int
|
|
||||||
j int
|
|
||||||
)
|
|
||||||
for i = 0; i < len(slice); i++ {
|
|
||||||
if expression(i, slice[i]) {
|
|
||||||
if reserve {
|
|
||||||
slice[j] = slice[i]
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !reserve {
|
|
||||||
slice[j] = slice[i]
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return slice[:j]
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterT 与 Filter 的功能相同,但是 reserve 被默认为 true
|
|
||||||
func FilterT[V any](slice []V, expression func(index int, item V) bool) []V {
|
|
||||||
return Filter(true, slice, expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterF 与 Filter 的功能相同,但是 reserve 被默认为 false
|
|
||||||
func FilterF[V any](slice []V, expression func(index int, item V) bool) []V {
|
|
||||||
return Filter(false, slice, expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterCopy 与 Filter 的功能相同,但是不会改变原切片,而是返回一个新的切片
|
|
||||||
func FilterCopy[V any](reserve bool, slice []V, expression func(index int, item V) bool) []V {
|
|
||||||
var s = make([]V, len(slice))
|
|
||||||
copy(s, slice)
|
|
||||||
return Filter(reserve, s, expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterTCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 true
|
|
||||||
func FilterTCopy[V any](slice []V, expression func(index int, item V) bool) []V {
|
|
||||||
return FilterCopy(true, slice, expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterFCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 false
|
|
||||||
func FilterFCopy[V any](slice []V, expression func(index int, item V) bool) []V {
|
|
||||||
return FilterCopy(false, slice, expression)
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package slice_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFilter(t *testing.T) {
|
|
||||||
s := []int{1, 2, 3, 4, 5}
|
|
||||||
s = slice.Filter(true, s, func(index int, item int) bool {
|
|
||||||
return item%2 == 0
|
|
||||||
})
|
|
||||||
if len(s) != 2 {
|
|
||||||
t.Error("Filter failed")
|
|
||||||
}
|
|
||||||
if s[0] != 2 {
|
|
||||||
t.Error("Filter failed")
|
|
||||||
}
|
|
||||||
if s[1] != 4 {
|
|
||||||
t.Error("Filter failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterCopy(t *testing.T) {
|
|
||||||
s := []int{1, 2, 3, 4, 5}
|
|
||||||
cp := slice.FilterCopy(true, s, func(index int, item int) bool {
|
|
||||||
return item%2 == 0
|
|
||||||
})
|
|
||||||
if len(s) != 5 {
|
|
||||||
t.Error("FilterCopy failed")
|
|
||||||
} else {
|
|
||||||
t.Log(s, cp)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
// GroupBy 返回分组后的切片
|
|
||||||
func GroupBy[T any, K comparable](collection []T, fn func(T) K) map[K][]T {
|
|
||||||
if len(collection) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make(map[K][]T, len(collection))
|
|
||||||
|
|
||||||
for _, item := range collection {
|
|
||||||
key := fn(item)
|
|
||||||
result[key] = append(result[key], item)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
// Map 将切片中的每一个元素转换为另一种类型的元素
|
|
||||||
// - slice: 待转换的切片
|
|
||||||
// - converter: 转换函数
|
|
||||||
//
|
|
||||||
// 这不会改变原有的切片,而是返回一个新的切片
|
|
||||||
func Map[V any, T any](slice []V, converter func(index int, item V) T) []T {
|
|
||||||
var s = make([]T, len(slice))
|
|
||||||
for i := 0; i < len(slice); i++ {
|
|
||||||
s[i] = converter(i, slice[i])
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewPriority 创建一个优先级切片
|
|
||||||
func NewPriority[V any](lengthAndCap ...int) *Priority[V] {
|
|
||||||
p := &Priority[V]{}
|
|
||||||
if len(lengthAndCap) > 0 {
|
|
||||||
var length = lengthAndCap[0]
|
|
||||||
var c int
|
|
||||||
if len(lengthAndCap) > 1 {
|
|
||||||
c = lengthAndCap[1]
|
|
||||||
}
|
|
||||||
p.items = make([]*PriorityItem[V], length, c)
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Priority 是一个优先级切片
|
|
||||||
type Priority[V any] struct {
|
|
||||||
items []*PriorityItem[V]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len 返回切片长度
|
|
||||||
func (slf *Priority[V]) Len() int {
|
|
||||||
return len(slf.items)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cap 返回切片容量
|
|
||||||
func (slf *Priority[V]) Cap() int {
|
|
||||||
return cap(slf.items)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear 清空切片
|
|
||||||
func (slf *Priority[V]) Clear() {
|
|
||||||
slf.items = slf.items[:0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append 添加元素
|
|
||||||
func (slf *Priority[V]) Append(v V, priority int) {
|
|
||||||
slf.items = append(slf.items, NewPriorityItem[V](v, priority))
|
|
||||||
slf.sort()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Appends 添加元素
|
|
||||||
func (slf *Priority[V]) Appends(priority int, vs ...V) {
|
|
||||||
for _, v := range vs {
|
|
||||||
slf.Append(v, priority)
|
|
||||||
}
|
|
||||||
slf.sort()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get 获取元素
|
|
||||||
func (slf *Priority[V]) Get(index int) *PriorityItem[V] {
|
|
||||||
return slf.items[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetValue 获取元素值
|
|
||||||
func (slf *Priority[V]) GetValue(index int) V {
|
|
||||||
return slf.items[index].Value()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPriority 获取元素优先级
|
|
||||||
func (slf *Priority[V]) GetPriority(index int) int {
|
|
||||||
return slf.items[index].Priority()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set 设置元素
|
|
||||||
func (slf *Priority[V]) Set(index int, value V, priority int) {
|
|
||||||
before := slf.items[index]
|
|
||||||
slf.items[index] = NewPriorityItem[V](value, priority)
|
|
||||||
if before.Priority() != priority {
|
|
||||||
slf.sort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetValue 设置元素值
|
|
||||||
func (slf *Priority[V]) SetValue(index int, value V) {
|
|
||||||
slf.items[index].v = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPriority 设置元素优先级
|
|
||||||
func (slf *Priority[V]) SetPriority(index int, priority int) {
|
|
||||||
slf.items[index].p = priority
|
|
||||||
slf.sort()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Action 直接操作切片,如果返回值不为 nil,则替换切片
|
|
||||||
func (slf *Priority[V]) Action(action func(items []*PriorityItem[V]) []*PriorityItem[V]) {
|
|
||||||
if len(slf.items) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if replace := action(slf.items); replace != nil {
|
|
||||||
slf.items = replace
|
|
||||||
slf.sort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Range 遍历切片,如果返回值为 false,则停止遍历
|
|
||||||
func (slf *Priority[V]) Range(action func(index int, item *PriorityItem[V]) bool) {
|
|
||||||
for i, item := range slf.items {
|
|
||||||
if !action(i, item) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RangeValue 遍历切片值,如果返回值为 false,则停止遍历
|
|
||||||
func (slf *Priority[V]) RangeValue(action func(index int, value V) bool) {
|
|
||||||
slf.Range(func(index int, item *PriorityItem[V]) bool {
|
|
||||||
return action(index, item.Value())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// RangePriority 遍历切片优先级,如果返回值为 false,则停止遍历
|
|
||||||
func (slf *Priority[V]) RangePriority(action func(index int, priority int) bool) {
|
|
||||||
slf.Range(func(index int, item *PriorityItem[V]) bool {
|
|
||||||
return action(index, item.Priority())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slice 返回切片
|
|
||||||
func (slf *Priority[V]) Slice() []V {
|
|
||||||
var vs []V
|
|
||||||
for _, item := range slf.items {
|
|
||||||
vs = append(vs, item.Value())
|
|
||||||
}
|
|
||||||
return vs
|
|
||||||
}
|
|
||||||
|
|
||||||
// String 返回切片字符串
|
|
||||||
func (slf *Priority[V]) String() string {
|
|
||||||
var vs []V
|
|
||||||
for _, item := range slf.items {
|
|
||||||
vs = append(vs, item.Value())
|
|
||||||
}
|
|
||||||
return fmt.Sprint(vs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort 排序
|
|
||||||
func (slf *Priority[V]) sort() {
|
|
||||||
if len(slf.items) <= 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sort.Slice(slf.items, func(i, j int) bool {
|
|
||||||
return slf.items[i].Priority() < slf.items[j].Priority()
|
|
||||||
})
|
|
||||||
for i := 0; i < len(slf.items); i++ {
|
|
||||||
if i == 0 {
|
|
||||||
slf.items[i].prev = nil
|
|
||||||
slf.items[i].next = slf.items[i+1]
|
|
||||||
} else if i == len(slf.items)-1 {
|
|
||||||
slf.items[i].prev = slf.items[i-1]
|
|
||||||
slf.items[i].next = nil
|
|
||||||
} else {
|
|
||||||
slf.items[i].prev = slf.items[i-1]
|
|
||||||
slf.items[i].next = slf.items[i+1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
// NewPriorityItem 创建一个优先级切片元素
|
|
||||||
func NewPriorityItem[V any](v V, priority int) *PriorityItem[V] {
|
|
||||||
return &PriorityItem[V]{v: v, p: priority}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PriorityItem 是一个优先级切片元素
|
|
||||||
type PriorityItem[V any] struct {
|
|
||||||
next *PriorityItem[V]
|
|
||||||
prev *PriorityItem[V]
|
|
||||||
v V
|
|
||||||
p int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value 返回元素值
|
|
||||||
func (p *PriorityItem[V]) Value() V {
|
|
||||||
return p.v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Priority 返回元素优先级
|
|
||||||
func (p *PriorityItem[V]) Priority() int {
|
|
||||||
return p.p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next 返回下一个元素
|
|
||||||
func (p *PriorityItem[V]) Next() *PriorityItem[V] {
|
|
||||||
return p.next
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prev 返回上一个元素
|
|
||||||
func (p *PriorityItem[V]) Prev() *PriorityItem[V] {
|
|
||||||
return p.prev
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package slice_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPriority_Append(t *testing.T) {
|
|
||||||
var s = slice.NewPriority[string]()
|
|
||||||
s.Append("name_1", 2)
|
|
||||||
s.Append("name_2", 1)
|
|
||||||
fmt.Println(s)
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reduce 将切片中的多个元素组合成一个单一值
|
|
||||||
// - start: 开始索引,如果为负数则从后往前计算,例如:-1 表示从最后一个元素开始向左遍历,1 表示从第二个元素开始
|
|
||||||
// - slice: 待组合的切片
|
|
||||||
// - reducer: 组合函数
|
|
||||||
func Reduce[V any, R generic.Number](start int, slice []V, reducer func(index int, item V, current R) R) (result R) {
|
|
||||||
length := len(slice)
|
|
||||||
|
|
||||||
if start >= length || -start > length {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if start < 0 {
|
|
||||||
for i := length + start; i >= 0; i-- {
|
|
||||||
result = reducer(i, slice[i], result)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := start; i < length; i++ {
|
|
||||||
result = reducer(i, slice[i], result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package slice_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestReduce(t *testing.T) {
|
|
||||||
s := []int{1, 2, 3, 4, 5}
|
|
||||||
sum := slice.Reduce(0, s, func(index int, item int, current int) int {
|
|
||||||
return current + item
|
|
||||||
})
|
|
||||||
t.Log(sum)
|
|
||||||
if sum != 15 {
|
|
||||||
t.Error("Reduce failed")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
import "math/rand"
|
|
||||||
|
|
||||||
// Shuffle 随机打乱切片
|
|
||||||
// - 该函数会改变传入的切片,如果不希望改变原切片,需要在函数调用之前手动复制一份或者使用 ShuffleCopy 函数
|
|
||||||
func Shuffle[T any](collection []T) []T {
|
|
||||||
rand.Shuffle(len(collection), func(i, j int) {
|
|
||||||
collection[i], collection[j] = collection[j], collection[i]
|
|
||||||
})
|
|
||||||
|
|
||||||
return collection
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShuffleCopy 返回随机打乱后的切片
|
|
||||||
// - 该函数不会改变原切片
|
|
||||||
func ShuffleCopy[T any](collection []T) []T {
|
|
||||||
if len(collection) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make([]T, len(collection))
|
|
||||||
perm := rand.Perm(len(collection))
|
|
||||||
|
|
||||||
for i, randIndex := range perm {
|
|
||||||
result[i] = collection[randIndex]
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
|
@ -1,328 +0,0 @@
|
||||||
package slice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Slice 切片类型
|
|
||||||
type Slice[V any] []V
|
|
||||||
|
|
||||||
// GetValue 获取特定索引的元素,如果索引超出范围则返回零值
|
|
||||||
func GetValue[V any](slice []V, i int) (v V) {
|
|
||||||
if i >= 0 && i < len(slice) {
|
|
||||||
return slice[i]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetValueHandle 获取特定索引的元素,并通过 handle 进入传入,如果索引超出范围则不会进入 handle
|
|
||||||
func GetValueHandle[V any](slice []V, i int, handle func(v V)) {
|
|
||||||
if i >= 0 && i < len(slice) {
|
|
||||||
handle(slice[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Del 删除特定索引的元素
|
|
||||||
func Del[V any](slice *[]V, index int) {
|
|
||||||
s := *slice
|
|
||||||
*slice = append(s[:index], s[index+1:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy 复制特定切片
|
|
||||||
// - 该函数已经可使用标准库 slices.Clone 代替,但是由于调用者可能繁多,所以该函数将不会被移除
|
|
||||||
func Copy[S ~[]V, V any](slice S) S {
|
|
||||||
if slice == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return append(slice[:0:0], slice...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyMatrix 复制二维数组
|
|
||||||
func CopyMatrix[S ~[][]V, V any](slice S) S {
|
|
||||||
if slice == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var result = make(S, len(slice))
|
|
||||||
for i := 0; i < len(slice); i++ {
|
|
||||||
result[i] = Copy(slice[i])
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert 在特定索引插入元素
|
|
||||||
func Insert[V any](slice *[]V, index int, value V) {
|
|
||||||
s := *slice
|
|
||||||
if index <= 0 {
|
|
||||||
*slice = append([]V{value}, s...)
|
|
||||||
} else if index >= len(s) {
|
|
||||||
*slice = append(s, value)
|
|
||||||
} else {
|
|
||||||
*slice = append(s[:index], append([]V{value}, s[index:]...)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move 移动特定索引
|
|
||||||
func Move[V any](slice *[]V, index, to int) {
|
|
||||||
s := *slice
|
|
||||||
v := s[index]
|
|
||||||
if index == to {
|
|
||||||
return
|
|
||||||
} else if to < index {
|
|
||||||
Del[V](slice, index)
|
|
||||||
Insert(slice, to, v)
|
|
||||||
} else {
|
|
||||||
Insert(slice, to, v)
|
|
||||||
Del[V](slice, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextLoop 返回 i 的下一个数组成员,当 i 达到数组长度时从 0 开始
|
|
||||||
// - 当 i 为 -1 时将返回第一个元素
|
|
||||||
func NextLoop[V any](slice []V, i int) (next int, value V) {
|
|
||||||
if i == -1 {
|
|
||||||
return 0, slice[0]
|
|
||||||
}
|
|
||||||
next = i + 1
|
|
||||||
if next == len(slice) {
|
|
||||||
next = 0
|
|
||||||
}
|
|
||||||
return next, slice[next]
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrevLoop 返回 i 的上一个数组成员,当 i 为 0 时从数组末尾开始
|
|
||||||
// - 当 i 为 -1 时将返回最后一个元素
|
|
||||||
func PrevLoop[V any](slice []V, i int) (prev int, value V) {
|
|
||||||
if i == -1 {
|
|
||||||
return len(slice) - 1, slice[len(slice)-1]
|
|
||||||
}
|
|
||||||
prev = i - 1
|
|
||||||
if prev == -1 {
|
|
||||||
prev = len(slice) - 1
|
|
||||||
}
|
|
||||||
return prev, slice[prev]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse 反转数组
|
|
||||||
func Reverse[V any](slice []V) {
|
|
||||||
for i := 0; i < len(slice)/2; i++ {
|
|
||||||
slice[i], slice[len(slice)-1-i] = slice[len(slice)-1-i], slice[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Distinct 去重
|
|
||||||
func Distinct[V any](slice []V) []V {
|
|
||||||
var result []V
|
|
||||||
for i := range slice {
|
|
||||||
flag := true
|
|
||||||
for j := range result {
|
|
||||||
if reflect.DeepEqual(slice[i], result[j]) {
|
|
||||||
flag = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if flag {
|
|
||||||
result = append(result, slice[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap 交换数组中的两个元素
|
|
||||||
func Swap[V any](slice []V, i, j int) {
|
|
||||||
slice[i], slice[j] = slice[j], slice[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToMap 将数组转换为 map
|
|
||||||
func ToMap[K comparable, V any](slice []V, key func(V) K) map[K]V {
|
|
||||||
m := make(map[K]V)
|
|
||||||
for _, v := range slice {
|
|
||||||
m[key(v)] = v
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSet 将数组转换为 set
|
|
||||||
func ToSet[V comparable](slice []V) map[V]struct{} {
|
|
||||||
m := make(map[V]struct{})
|
|
||||||
for _, v := range slice {
|
|
||||||
m[v] = struct{}{}
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge 合并多个数组
|
|
||||||
func Merge[V any](slices ...[]V) []V {
|
|
||||||
var slice []V
|
|
||||||
for _, s := range slices {
|
|
||||||
slice = append(slice, s...)
|
|
||||||
}
|
|
||||||
return slice
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStartPart 获取数组的前 n 个元素
|
|
||||||
func GetStartPart[V any](slice []V, n int) []V {
|
|
||||||
if n > len(slice) {
|
|
||||||
n = len(slice)
|
|
||||||
}
|
|
||||||
return slice[:n]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEndPart 获取数组的后 n 个元素
|
|
||||||
func GetEndPart[V any](slice []V, n int) []V {
|
|
||||||
if n > len(slice) {
|
|
||||||
n = len(slice)
|
|
||||||
}
|
|
||||||
return slice[len(slice)-n:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPart 获取指定区间的元素
|
|
||||||
func GetPart[V any](slice []V, start, end int) []V {
|
|
||||||
if start < 0 {
|
|
||||||
start = 0
|
|
||||||
}
|
|
||||||
if end > len(slice) {
|
|
||||||
end = len(slice)
|
|
||||||
}
|
|
||||||
return slice[start:end]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains 判断数组是否包含某个元素
|
|
||||||
func Contains[V comparable](slice []V, value V) bool {
|
|
||||||
for _, v := range slice {
|
|
||||||
if v == value {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainsAny 判断数组是否包含某个元素
|
|
||||||
func ContainsAny[V any](slice []V, values V) bool {
|
|
||||||
for _, v := range slice {
|
|
||||||
if reflect.DeepEqual(v, values) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIndex 判断数组是否包含某个元素,如果包含则返回索引
|
|
||||||
func GetIndex[V comparable](slice []V, value V) int {
|
|
||||||
for i, v := range slice {
|
|
||||||
if v == value {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIndexAny 判断数组是否包含某个元素,如果包含则返回索引
|
|
||||||
func GetIndexAny[V any](slice []V, values V) int {
|
|
||||||
for i, v := range slice {
|
|
||||||
if reflect.DeepEqual(v, values) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combinations 获取给定数组的所有组合,包括重复元素的组合
|
|
||||||
func Combinations[T any](a []T) [][]T {
|
|
||||||
var result [][]T
|
|
||||||
n := len(a)
|
|
||||||
for i := 0; i < (1 << n); i++ {
|
|
||||||
var currentCombination []T
|
|
||||||
for j := 0; j < n; j++ {
|
|
||||||
if (i & (1 << j)) != 0 {
|
|
||||||
currentCombination = append(currentCombination, a[j])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = append(result, currentCombination)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// CombinationsPari 从给定的两个数组中按照特定数量得到所有组合后,再将两个数组的组合进行组合
|
|
||||||
// - 例如从数组 A 中得到所有数量为2的组合,从数组 B 中得到所有数量为3的组合,再将两个组合进行组合,得到所有数量为5的组合
|
|
||||||
func CombinationsPari[T any](a, b []T, aCount, bCount int) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
var as, bs = LimitedCombinations(a, aCount, aCount), LimitedCombinations(b, bCount, bCount)
|
|
||||||
for _, asi := range as {
|
|
||||||
for _, bsi := range bs {
|
|
||||||
combinations = append(combinations, append(asi, bsi...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return combinations
|
|
||||||
}
|
|
||||||
|
|
||||||
// LimitedCombinations 获取给定数组的所有组合,且每个组合的成员数量限制在指定范围内
|
|
||||||
func LimitedCombinations[T any](a []T, minSize, maxSize int) [][]T {
|
|
||||||
n := len(a)
|
|
||||||
if n == 0 || minSize <= 0 || maxSize <= 0 || minSize > maxSize {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var result [][]T
|
|
||||||
var currentCombination []T
|
|
||||||
|
|
||||||
var backtrack func(startIndex int, currentSize int)
|
|
||||||
backtrack = func(startIndex int, currentSize int) {
|
|
||||||
if currentSize >= minSize && currentSize <= maxSize {
|
|
||||||
combination := make([]T, len(currentCombination))
|
|
||||||
copy(combination, currentCombination)
|
|
||||||
result = append(result, combination)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := startIndex; i < n; i++ {
|
|
||||||
currentCombination = append(currentCombination, a[i])
|
|
||||||
backtrack(i+1, currentSize+1)
|
|
||||||
currentCombination = currentCombination[:len(currentCombination)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
backtrack(0, 0)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsIntersectWithCheck 判断两个切片是否有交集
|
|
||||||
func IsIntersectWithCheck[T any](a, b []T, checkHandle func(a, b T) bool) bool {
|
|
||||||
for _, a := range a {
|
|
||||||
for _, b := range b {
|
|
||||||
if checkHandle(a, b) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsIntersect 判断两个切片是否有交集
|
|
||||||
func IsIntersect[T any](a, b []T) bool {
|
|
||||||
for _, a := range a {
|
|
||||||
for _, b := range b {
|
|
||||||
if reflect.DeepEqual(a, b) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubWithCheck 获取移除指定元素后的切片
|
|
||||||
// - checkHandle 返回 true 表示需要移除
|
|
||||||
func SubWithCheck[T any](a, b []T, checkHandle func(a, b T) bool) []T {
|
|
||||||
var result []T
|
|
||||||
for _, a := range a {
|
|
||||||
flag := false
|
|
||||||
for _, b := range b {
|
|
||||||
if checkHandle(a, b) {
|
|
||||||
flag = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !flag {
|
|
||||||
result = append(result, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package slice_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLimitedCombinations(t *testing.T) {
|
|
||||||
c := slice.LimitedCombinations([]int{1, 2, 3, 4, 5}, 3, 3)
|
|
||||||
for _, v := range c {
|
|
||||||
fmt.Println(v)
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue