Merge branch 'develop'

This commit is contained in:
kercylan98 2023-09-08 20:15:06 +08:00
commit 854ee9adf8
35 changed files with 1174 additions and 522 deletions

View File

@ -1,7 +1,7 @@
package task package task
const ( const (
StateAccept State = iota // 已接受 StateAccept State = iota + 1 // 已接受
StateFinish // 已完成 StateFinish // 已完成
StateReward // 已领取 StateReward // 已领取
StateFail // 已失败 StateFail // 已失败

View File

@ -127,6 +127,8 @@ type Conn struct {
type connection struct { type connection struct {
server *Server server *Server
mutex *sync.Mutex mutex *sync.Mutex
close sync.Once
closed bool
remoteAddr net.Addr remoteAddr net.Addr
ip string ip string
ws *websocket.Conn ws *websocket.Conn
@ -143,21 +145,6 @@ func (slf *Conn) IsEmpty() bool {
return slf.ws == nil && slf.gn == nil && slf.kcp == nil && slf.gw == nil return slf.ws == nil && slf.gn == nil && slf.kcp == nil && slf.gw == nil
} }
// Reuse 重用连接
// - 重用连接时,会将当前连接的数据复制到新连接中
// - 通常在于连接断开后,重新连接时使用
func (slf *Conn) Reuse(conn *Conn) {
slf.Close()
slf.remoteAddr = conn.remoteAddr
slf.ip = conn.ip
slf.ws = conn.ws
slf.gn = conn.gn
slf.kcp = conn.kcp
slf.data = conn.data
slf.packetPool = conn.packetPool
slf.packets = conn.packets
}
// RemoteAddr 获取远程地址 // RemoteAddr 获取远程地址
func (slf *Conn) RemoteAddr() net.Addr { func (slf *Conn) RemoteAddr() net.Addr {
return slf.remoteAddr return slf.remoteAddr
@ -174,8 +161,15 @@ func (slf *Conn) GetIP() string {
return slf.ip return slf.ip
} }
// IsClosed 是否已经关闭
func (slf *Conn) IsClosed() bool {
return slf.closed
}
// Close 关闭连接 // Close 关闭连接
func (slf *Conn) Close() { func (slf *Conn) Close(err ...error) {
slf.close.Do(func() {
slf.closed = true
if slf.ws != nil { if slf.ws != nil {
_ = slf.ws.Close() _ = slf.ws.Close()
} else if slf.gn != nil { } else if slf.gn != nil {
@ -191,6 +185,12 @@ func (slf *Conn) Close() {
close(slf.packets) close(slf.packets)
slf.packets = nil slf.packets = nil
} }
if len(err) > 0 {
slf.server.OnConnectionClosedEvent(slf, err[0])
return
}
slf.server.OnConnectionClosedEvent(slf, nil)
})
} }
// SetData 设置连接数据,该数据将在连接关闭前始终存在 // SetData 设置连接数据,该数据将在连接关闭前始终存在
@ -276,9 +276,6 @@ func (slf *Conn) writeLoop(wait *sync.WaitGroup) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
slf.Close() slf.Close()
// TODO: 以下代码是否需要?
// log.Error("WriteLoop", log.Any("Error", err))
// debug.PrintStack()
} }
}() }()
wait.Done() wait.Done()

View File

@ -184,12 +184,11 @@ func (slf *event) RegConnectionClosedEvent(handle ConnectionClosedEventHandle, p
func (slf *event) OnConnectionClosedEvent(conn *Conn, err any) { func (slf *event) OnConnectionClosedEvent(conn *Conn, err any) {
PushSystemMessage(slf.Server, func() { PushSystemMessage(slf.Server, func() {
slf.Server.online.Delete(conn.GetID())
slf.connectionClosedEventHandles.RangeValue(func(index int, value ConnectionClosedEventHandle) bool { slf.connectionClosedEventHandles.RangeValue(func(index int, value ConnectionClosedEventHandle) bool {
value(slf.Server, conn, err) value(slf.Server, conn, err)
return true return true
}) })
conn.Close()
slf.Server.online.Delete(conn.GetID())
}, "ConnectionClosedEvent") }, "ConnectionClosedEvent")
} }

View File

@ -26,7 +26,8 @@ func (slf *gNet) OnOpened(c gnet.Conn) (out []byte, action gnet.Action) {
} }
func (slf *gNet) OnClosed(c gnet.Conn, err error) (action gnet.Action) { func (slf *gNet) OnClosed(c gnet.Conn, err error) (action gnet.Action) {
slf.OnConnectionClosedEvent(c.Context().(*Conn), err) conn := c.Context().(*Conn)
conn.Close(err)
return return
} }

View File

@ -2,9 +2,9 @@ package server
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"github.com/kercylan98/minotaur/utils/hash" "github.com/kercylan98/minotaur/utils/hash"
"github.com/kercylan98/minotaur/utils/super"
"reflect" "reflect"
) )
@ -42,7 +42,7 @@ var messageNames = map[MessageType]string{
} }
const ( const (
MessageErrorActionNone MessageErrorAction = iota // 错误消息类型操作:将不会被进行任何特殊处理,仅进行日志输出 MessageErrorActionNone MessageErrorAction = iota + 1 // 错误消息类型操作:将不会被进行任何特殊处理,仅进行日志输出
MessageErrorActionShutdown // 错误消息类型操作:当接收到该类型的操作时,服务器将执行 Server.shutdown 函数 MessageErrorActionShutdown // 错误消息类型操作:当接收到该类型的操作时,服务器将执行 Server.shutdown 函数
) )
@ -85,6 +85,22 @@ func (slf *Message) MessageType() MessageType {
return slf.t return slf.t
} }
// AttrsString 返回消息属性的字符串表示
func (slf *Message) AttrsString() string {
var attrs = make([]any, 0, len(slf.attrs))
for _, attr := range slf.attrs {
if tof := reflect.TypeOf(attr); tof.Kind() == reflect.Func {
attrs = append(attrs, tof.String())
continue
}
attrs = append(attrs, attr)
}
if len(attrs) == 0 {
return "NoneAttr"
}
return string(super.MarshalJSON(attrs))
}
// String 返回消息的字符串表示 // String 返回消息的字符串表示
func (slf *Message) String() string { func (slf *Message) String() string {
var attrs = make([]any, 0, len(slf.attrs)) var attrs = make([]any, 0, len(slf.attrs))
@ -94,22 +110,8 @@ func (slf *Message) String() string {
} }
attrs = append(attrs, attr) attrs = append(attrs, attr)
} }
var s string
switch slf.t {
case MessageTypePacket:
if len(attrs) > 1 {
s = messagePacketVisualization(attrs[1].([]byte))
}
default:
if len(slf.attrs) == 0 {
s = "NoneAttr"
} else {
raw, _ := json.Marshal(attrs)
s = string(raw)
}
}
return fmt.Sprintf("[%s] %s", slf.t, s) return fmt.Sprintf("[%s] %s", slf.t, slf.AttrsString())
} }
// String 返回消息类型的字符串表示 // String 返回消息类型的字符串表示

View File

@ -205,14 +205,21 @@ func (slf *Server) Run(addr string) error {
go func(conn *Conn) { go func(conn *Conn) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
slf.OnConnectionClosedEvent(conn, err) e, ok := err.(error)
if !ok {
e = fmt.Errorf("%v", err)
}
conn.Close(e)
} }
}() }()
buf := make([]byte, 4096) buf := make([]byte, 4096)
for { for !conn.IsClosed() {
n, err := conn.kcp.Read(buf) n, err := conn.kcp.Read(buf)
if err != nil { if err != nil {
if conn.IsClosed() {
break
}
panic(err) panic(err)
} }
PushPacketMessage(slf, conn, 0, buf[:n]) PushPacketMessage(slf, conn, 0, buf[:n])
@ -292,15 +299,22 @@ func (slf *Server) Run(addr string) error {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
slf.OnConnectionClosedEvent(conn, err) e, ok := err.(error)
if !ok {
e = fmt.Errorf("%v", err)
}
conn.Close(e)
} }
}() }()
for { for !conn.IsClosed() {
if err := ws.SetReadDeadline(super.If(slf.websocketReadDeadline <= 0, times.Zero, time.Now().Add(slf.websocketReadDeadline))); err != nil { if err := ws.SetReadDeadline(super.If(slf.websocketReadDeadline <= 0, times.Zero, time.Now().Add(slf.websocketReadDeadline))); err != nil {
panic(err) panic(err)
} }
messageType, packet, readErr := ws.ReadMessage() messageType, packet, readErr := ws.ReadMessage()
if readErr != nil { if readErr != nil {
if conn.IsClosed() {
break
}
panic(readErr) panic(readErr)
} }
if len(slf.supportMessageTypes) > 0 && !slf.supportMessageTypes[messageType] { if len(slf.supportMessageTypes) > 0 && !slf.supportMessageTypes[messageType] {
@ -559,7 +573,7 @@ func (slf *Server) ShuntChannelFreed(channelGuid int64) {
// pushMessage 向服务器中写入特定类型的消息,需严格遵守消息属性要求 // pushMessage 向服务器中写入特定类型的消息,需严格遵守消息属性要求
func (slf *Server) pushMessage(message *Message) { func (slf *Server) pushMessage(message *Message) {
if slf.messagePool.IsClose() || slf.isShutdown.Load() || !slf.OnMessageExecBeforeEvent(message) { if slf.messagePool.IsClose() || !slf.OnMessageExecBeforeEvent(message) {
slf.messagePool.Release(message) slf.messagePool.Release(message)
return return
} }
@ -578,12 +592,13 @@ func (slf *Server) pushMessage(message *Message) {
defer slf.OnShuntChannelCreatedEvent(channelGuid) defer slf.OnShuntChannelCreatedEvent(channelGuid)
} }
if channel != nil { if channel != nil {
slf.messageCounter.Add(1)
channel <- message channel <- message
return return
} }
} }
slf.messageCounter.Add(1)
slf.messageChannel <- message slf.messageChannel <- message
} }
func (slf *Server) low(message *Message, present time.Time, expect time.Duration, messageReplace ...string) { func (slf *Server) low(message *Message, present time.Time, expect time.Duration, messageReplace ...string) {
@ -602,7 +617,6 @@ func (slf *Server) low(message *Message, present time.Time, expect time.Duration
// dispatchMessage 消息分发 // dispatchMessage 消息分发
func (slf *Server) dispatchMessage(msg *Message) { func (slf *Server) dispatchMessage(msg *Message) {
slf.messageCounter.Add(1)
var ( var (
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
@ -623,7 +637,7 @@ func (slf *Server) dispatchMessage(msg *Message) {
defer func(msg *Message) { defer func(msg *Message) {
if err := recover(); err != nil { if err := recover(); err != nil {
stack := string(debug.Stack()) stack := string(debug.Stack())
log.Error("Server", log.String("MessageType", messageNames[msg.t]), log.Any("MessageAttrs", msg.attrs), log.Any("error", err), log.String("stack", stack)) log.Error("Server", log.String("MessageType", messageNames[msg.t]), log.Any("MessageAttrs", msg.AttrsString()), log.Any("error", err), log.String("stack", stack))
fmt.Println(stack) fmt.Println(stack)
if e, ok := err.(error); ok { if e, ok := err.(error); ok {
slf.OnMessageErrorEvent(msg, e) slf.OnMessageErrorEvent(msg, e)

View File

@ -21,6 +21,15 @@ func TestNew(t *testing.T) {
} }
return true return true
}) })
srv.RegConnectionClosedEvent(func(srv *server.Server, conn *server.Conn, err any) {
fmt.Println("关闭", conn.GetID(), err, "Count", srv.GetOnlineCount())
})
srv.RegConnectionOpenedEvent(func(srv *server.Server, conn *server.Conn) {
if srv.GetOnlineCount() > 1 {
conn.Close()
}
})
srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) { srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) {
conn.Write(packet) conn.Write(packet)
}) })

27
utils/hash/chunk.go Normal file
View File

@ -0,0 +1,27 @@
package hash
// Chunk 将哈希表按照指定大小分块
// - m: 待分块的哈希表
// - size: 每块的大小
func Chunk[K comparable, V any](m map[K]V, size int) []map[K]V {
if len(m) == 0 {
return nil
}
var (
i int
j int
)
chunks := make([]map[K]V, (len(m)-1)/size+1)
for i = 0; i < len(m); i += size {
chunks[j] = make(map[K]V, size)
for key, value := range m {
if i <= j*size && j*size < i+size {
chunks[j][key] = value
}
}
j++
}
return chunks
}

56
utils/hash/drop.go Normal file
View File

@ -0,0 +1,56 @@
package hash
// RandomDrop 随机删除哈希表中的指定数量的元素
// - 该函数会修改原哈希表,如果不想修改原哈希表,请使用 RandomDropCopy
func RandomDrop[K comparable, V any](n int, hash map[K]V) map[K]V {
if n <= 0 || len(hash) <= n {
return hash
}
for k := range hash {
if n <= 0 {
break
}
delete(hash, k)
n--
}
return hash
}
// RandomDropCopy 随机删除哈希表中的指定数量的元素
// - 该函数不会修改原哈希表,如果想修改原哈希表,请使用 RandomDrop
func RandomDropCopy[K comparable, V any](n int, m map[K]V) map[K]V {
if n <= 0 || len(m) <= n {
return map[K]V{}
}
var nm = make(map[K]V, len(m))
for k, v := range m {
if n <= 0 {
break
}
nm[k] = v
n--
}
return nm
}
// DropBy 从哈希表中删除指定的元素
func DropBy[K comparable, V any](m map[K]V, fn func(key K, value V) bool) map[K]V {
for k, v := range m {
if fn(k, v) {
delete(m, k)
}
}
return m
}
// DropByCopy 与 DropBy 功能相同,但是该函数不会修改原哈希表
func DropByCopy[K comparable, V any](m map[K]V, fn func(key K, value V) bool) map[K]V {
var nm = make(map[K]V, len(m))
for k, v := range m {
if fn(k, v) {
continue
}
nm[k] = v
}
return nm
}

47
utils/hash/each.go Normal file
View File

@ -0,0 +1,47 @@
package hash
// Each 根据传入的 abort 遍历 m如果 iterator 返回值与 abort 相同,则停止遍历
func Each[K comparable, V any](abort bool, m map[K]V, iterator func(i int, key K, item V) bool) {
i := 0
for k, v := range m {
if iterator(i, k, v) == abort {
break
}
i++
}
}
// EachT 与 Each 的功能相同,但是 abort 被默认为 true
func EachT[K comparable, V any](m map[K]V, iterator func(i int, key K, item V) bool) {
Each(true, m, iterator)
}
// EachF 与 Each 的功能相同,但是 abort 被默认为 false
func EachF[K comparable, V any](m map[K]V, iterator func(i int, key K, item V) bool) {
Each(false, m, iterator)
}
// EachResult 根据传入的 abort 遍历 m得到遍历的结果如果 iterator 返回值中的 bool 值与 abort 相同,则停止遍历,并返回当前已积累的结果
func EachResult[K comparable, V any, R any](abort bool, m map[K]V, iterator func(i int, key K, item V) (R, bool)) []R {
var result []R
i := 0
for k, v := range m {
r, ok := iterator(i, k, v)
result = append(result, r)
if ok == abort {
break
}
i++
}
return result
}
// EachResultT 与 EachResult 的功能相同,但是 abort 被默认为 true
func EachResultT[K comparable, V any, R any](m map[K]V, iterator func(i int, key K, item V) (R, bool)) []R {
return EachResult(true, m, iterator)
}
// EachResultF 与 EachResult 的功能相同,但是 abort 被默认为 false
func EachResultF[K comparable, V any, R any](m map[K]V, iterator func(i int, key K, item V) (R, bool)) []R {
return EachResult(false, m, iterator)
}

56
utils/hash/filter.go Normal file
View File

@ -0,0 +1,56 @@
package hash
// Filter 根据特定的表达式过滤哈希表成员
// - reserve: 是否保留符合条件的成员
// - m: 待过滤的哈希表
// - expression: 过滤表达式
//
// 这个函数的作用是遍历输入哈希表 m然后根据 expression 函数的返回值来决定是否保留每个元素。具体来说
// - 如果 expression 返回 true 并且 reserve 也是 true那么元素会被保留
// - 如果 expression 返回 false 并且 reserve 是 false那么元素也会被保留
//
// 该没有创建新的内存空间或进行元素复制,所以整个操作相当高效。同时,由于 m 和 map 实际上共享底层的数组,因此这个函数会改变传入的 map。如果不希望改变原哈希表需要在函数调用之前手动复制一份或者使用 FilterCopy 函数。
func Filter[K comparable, V any](reserve bool, m map[K]V, expression func(key K, value V) bool) map[K]V {
if len(m) == 0 {
return m
}
for key, value := range m {
if !expression(key, value) {
delete(m, key)
}
}
return m
}
// FilterT 与 Filter 的功能相同,但是 reserve 被默认为 true
func FilterT[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V {
return Filter(true, m, expression)
}
// FilterF 与 Filter 的功能相同,但是 reserve 被默认为 false
func FilterF[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V {
return Filter(false, m, expression)
}
// FilterCopy 与 Filter 的功能相同,但是不会改变原哈希表,而是返回一个新的哈希表
func FilterCopy[K comparable, V any](reserve bool, m map[K]V, expression func(key K, value V) bool) map[K]V {
var res = map[K]V{}
for key, value := range m {
if expression(key, value) {
res[key] = value
}
}
return res
}
// FilterTCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 true
func FilterTCopy[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V {
return FilterCopy(true, m, expression)
}
// FilterFCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 false
func FilterFCopy[K comparable, V any](m map[K]V, expression func(key K, value V) bool) map[K]V {
return FilterCopy(false, m, expression)
}

View File

@ -70,11 +70,12 @@ func (slf *Analyzer) IncreaseNonRepeat(key string, record R, recordKey string, d
for _, v := range dimension { for _, v := range dimension {
dvs = append(dvs, record.GetString(v)) dvs = append(dvs, record.GetString(v))
} }
dk := strings.Join(dvs, "_") dk := strings.Join(append([]string{key}, dvs...), "_")
if _, e := slf.repeat[dk]; e { if _, e := slf.repeat[dk]; e {
slf.m.Unlock() slf.m.Unlock()
return return
} }
slf.repeat[dk] = struct{}{}
slf.m.Unlock() slf.m.Unlock()
slf.Increase(key, record, recordKey) slf.Increase(key, record, recordKey)
} }
@ -89,11 +90,12 @@ func (slf *Analyzer) IncreaseValueNonRepeat(key string, record R, value float64,
for _, v := range dimension { for _, v := range dimension {
dvs = append(dvs, record.GetString(v)) dvs = append(dvs, record.GetString(v))
} }
dk := strings.Join(dvs, "_") dk := strings.Join(append([]string{key}, dvs...), "_")
if _, e := slf.repeat[dk]; e { if _, e := slf.repeat[dk]; e {
slf.m.Unlock() slf.m.Unlock()
return return
} }
slf.repeat[dk] = struct{}{}
slf.m.Unlock() slf.m.Unlock()
slf.IncreaseValue(key, value) slf.IncreaseValue(key, value)
} }

View File

@ -1,6 +1,10 @@
package survey package survey
import "github.com/tidwall/gjson" import (
"github.com/kercylan98/minotaur/utils/times"
"github.com/tidwall/gjson"
"time"
)
type ( type (
Result = gjson.Result Result = gjson.Result
@ -9,6 +13,11 @@ type (
// R 记录器所记录的一条数据 // R 记录器所记录的一条数据
type R string type R string
// GetTime 获取该记录的时间
func (slf R) GetTime(layout string) time.Time {
return times.GetTimeFromString(string(slf)[:len(layout)], layout)
}
// Get 获取指定 key 的值 // Get 获取指定 key 的值
// - 当 key 为嵌套 key 时,使用 . 进行分割例如a.b.c // - 当 key 为嵌套 key 时,使用 . 进行分割例如a.b.c
// - 更多用法参考https://github.com/tidwall/gjson // - 更多用法参考https://github.com/tidwall/gjson

View File

@ -127,3 +127,18 @@ func Analyze(filePath string, handle func(analyzer *Analyzer, record R)) *Report
return newReport(analyzer) return newReport(analyzer)
} }
// AnalyzeMulti 与 Analyze 类似,但是可以分析多个文件
func AnalyzeMulti(filePaths []string, handle func(analyzer *Analyzer, record R)) *Report {
analyzer := new(Analyzer)
for _, filePath := range filePaths {
err := file.ReadLineWithParallel(filePath, 1*1024*1024*1024, func(s string) {
handle(analyzer, R(s))
})
if err != nil {
panic(err)
}
}
return newReport(analyzer)
}

18
utils/slice/chunk.go Normal file
View File

@ -0,0 +1,18 @@
package slice
// Chunk 返回分块后的切片
func Chunk[T any](collection []T, size int) [][]T {
if len(collection) == 0 {
return nil
}
if size < 1 {
panic("size must be greater than 0")
}
result := make([][]T, 0, (len(collection)+size-1)/size)
for size < len(collection) {
collection, result = collection[size:], append(result, collection[0:size])
}
return append(result, collection)
}

40
utils/slice/drop.go Normal file
View File

@ -0,0 +1,40 @@
package slice
// Drop 从切片中删除指定数量的元素
// - start: 起始位置
// - n: 删除的元素数量
// - slice: 待删除元素的切片
//
// 关于 start 的取值:
// - 当 start < 0 时start 将会从右至左计数,即 -1 表示最后一个元素,-2 表示倒数第二个元素,以此类推
func Drop[V any](start, n int, slice []V) []V {
var s = make([]V, len(slice))
copy(s, slice)
if start < 0 {
start = len(s) + start - n + 1
if start < 0 {
start = 0
}
}
end := start + n
if end > len(s) {
end = len(s)
}
return append(s[:start], s[end:]...)
}
// DropBy 从切片中删除指定的元素
// - slice: 待删除元素的切片
func DropBy[V any](slice []V, fn func(index int, value V) bool) []V {
var s = make([]V, len(slice))
copy(s, slice)
for i := 0; i < len(s); i++ {
if fn(i, s[i]) {
s = append(s[:i], s[i+1:]...)
i--
}
}
return s
}

11
utils/slice/drop_test.go Normal file
View File

@ -0,0 +1,11 @@
package slice_test
import (
"github.com/kercylan98/minotaur/utils/slice"
"testing"
)
func TestDrop(t *testing.T) {
s := []int{1, 2, 3, 4, 5}
t.Log(s, slice.Drop(1, 3, s))
}

85
utils/slice/each.go Normal file
View File

@ -0,0 +1,85 @@
package slice
// Each 根据传入的 abort 遍历 slice如果 iterator 返回值与 abort 相同,则停止遍历
func Each[V any](abort bool, slice []V, iterator func(index int, item V) bool) {
for i := 0; i < len(slice); i++ {
if iterator(i, slice[i]) == abort {
break
}
}
}
// EachT 与 Each 的功能相同,但是 abort 被默认为 true
func EachT[V any](slice []V, iterator func(index int, item V) bool) {
Each(true, slice, iterator)
}
// EachF 与 Each 的功能相同,但是 abort 被默认为 false
func EachF[V any](slice []V, iterator func(index int, item V) bool) {
Each(false, slice, iterator)
}
// EachReverse 根据传入的 abort 从后往前遍历 slice如果 iterator 返回值与 abort 相同,则停止遍历
func EachReverse[V any](abort bool, slice []V, iterator func(index int, item V) bool) {
for i := len(slice) - 1; i >= 0; i-- {
if iterator(i, slice[i]) == abort {
break
}
}
}
// EachReverseT 与 EachReverse 的功能相同,但是 abort 被默认为 true
func EachReverseT[V any](slice []V, iterator func(index int, item V) bool) {
EachReverse(true, slice, iterator)
}
// EachReverseF 与 EachReverse 的功能相同,但是 abort 被默认为 false
func EachReverseF[V any](slice []V, iterator func(index int, item V) bool) {
EachReverse(false, slice, iterator)
}
// EachResult 根据传入的 abort 遍历 slice得到遍历的结果如果 iterator 返回值中的 bool 值与 abort 相同,则停止遍历,并返回当前已积累的结果
func EachResult[V any, R any](abort bool, slice []V, iterator func(index int, item V) (R, bool)) []R {
var result []R
for i := 0; i < len(slice); i++ {
r, ok := iterator(i, slice[i])
result = append(result, r)
if ok == abort {
break
}
}
return result
}
// EachResultT 与 EachResult 的功能相同,但是 abort 被默认为 true
func EachResultT[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R {
return EachResult(true, slice, iterator)
}
// EachResultF 与 EachResult 的功能相同,但是 abort 被默认为 false
func EachResultF[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R {
return EachResult(false, slice, iterator)
}
// EachResultReverse 根据传入的 abort 从后往前遍历 slice得到遍历的结果如果 iterator 返回值中的 bool 值与 abort 相同,则停止遍历,并返回当前已积累的结果
func EachResultReverse[V any, R any](abort bool, slice []V, iterator func(index int, item V) (R, bool)) []R {
var result []R
for i := len(slice) - 1; i >= 0; i-- {
r, ok := iterator(i, slice[i])
result = append(result, r)
if ok == abort {
break
}
}
return result
}
// EachResultReverseT 与 EachResultReverse 的功能相同,但是 abort 被默认为 true
func EachResultReverseT[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R {
return EachResultReverse(true, slice, iterator)
}
// EachResultReverseF 与 EachResultReverse 的功能相同,但是 abort 被默认为 false
func EachResultReverseF[V any, R any](slice []V, iterator func(index int, item V) (R, bool)) []R {
return EachResultReverse(false, slice, iterator)
}

58
utils/slice/fill.go Normal file
View File

@ -0,0 +1,58 @@
package slice
// FillBy 用指定的值填充切片
// - slice: 待填充的切片
func FillBy[V any](slice []V, fn func(index int, value V) V) []V {
for i, v := range slice {
slice[i] = fn(i, v)
}
return slice
}
// FillByCopy 与 FillBy 的功能相同,但是不会改变原切片,而是返回一个新的切片
func FillByCopy[V any](slice []V, fn func(index int, value V) V) []V {
var s = make([]V, len(slice))
copy(s, slice)
return FillBy(s, fn)
}
// FillUntil 用指定的值填充切片,如果 fn 返回的 bool 值与 abort 相同,则停止填充
// - abort: 填充中止条件
// - slice: 待填充的切片
func FillUntil[V any](abort bool, slice []V, fn func(index int, value V) (V, bool)) []V {
for i, v := range slice {
if value, b := fn(i, v); b == abort {
break
} else {
slice[i] = value
}
}
return slice
}
// FillUntilCopy 与 FillUntil 的功能相同,但不会改变原切片,而是返回一个新的切片
func FillUntilCopy[V any](abort bool, slice []V, fn func(index int, value V) (V, bool)) []V {
var s = make([]V, len(slice))
copy(s, slice)
return FillUntil(abort, s, fn)
}
// FillUntilT 是 FillUntil 的简化版本,其中 abort 参数为 true
func FillUntilT[V any](slice []V, fn func(index int, value V) (V, bool)) []V {
return FillUntil(true, slice, fn)
}
// FillUntilF 是 FillUntil 的简化版本,其中 abort 参数为 false
func FillUntilF[V any](slice []V, fn func(index int, value V) (V, bool)) []V {
return FillUntil(false, slice, fn)
}
// FillUntilTCopy 是 FillUntilCopy 的简化版本,其中 abort 参数为 true
func FillUntilTCopy[V any](slice []V, fn func(index int, value V) (V, bool)) []V {
return FillUntilCopy(true, slice, fn)
}
// FillUntilFCopy 是 FillUntilCopy 的简化版本,其中 abort 参数为 false
func FillUntilFCopy[V any](slice []V, fn func(index int, value V) (V, bool)) []V {
return FillUntilCopy(false, slice, fn)
}

64
utils/slice/filter.go Normal file
View File

@ -0,0 +1,64 @@
package slice
// Filter 根据特定的表达式过滤切片成员
// - reserve: 是否保留符合条件的成员
// - slice: 待过滤的切片
// - expression: 过滤表达式
//
// 这个函数的作用是遍历输入切片 slice然后根据 expression 函数的返回值来决定是否保留每个元素。具体来说
// - 如果 expression 返回 true 并且 reserve 也是 true那么元素会被保留
// - 如果 expression 返回 false 并且 reserve 是 false那么元素也会被保留
//
// 该没有创建新的内存空间或进行元素复制,所以整个操作相当高效。同时,由于 s 和 slice 实际上共享底层的数组,因此这个函数会改变传入的 slice。如果不希望改变原切片需要在函数调用之前手动复制一份或者使用 FilterCopy 函数。
func Filter[V any](reserve bool, slice []V, expression func(index int, item V) bool) []V {
if len(slice) == 0 {
return slice
}
var (
i int
j int
)
for i = 0; i < len(slice); i++ {
if expression(i, slice[i]) {
if reserve {
slice[j] = slice[i]
j++
}
} else {
if !reserve {
slice[j] = slice[i]
j++
}
}
}
return slice[:j]
}
// FilterT 与 Filter 的功能相同,但是 reserve 被默认为 true
func FilterT[V any](slice []V, expression func(index int, item V) bool) []V {
return Filter(true, slice, expression)
}
// FilterF 与 Filter 的功能相同,但是 reserve 被默认为 false
func FilterF[V any](slice []V, expression func(index int, item V) bool) []V {
return Filter(false, slice, expression)
}
// FilterCopy 与 Filter 的功能相同,但是不会改变原切片,而是返回一个新的切片
func FilterCopy[V any](reserve bool, slice []V, expression func(index int, item V) bool) []V {
var s = make([]V, len(slice))
copy(s, slice)
return Filter(reserve, s, expression)
}
// FilterTCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 true
func FilterTCopy[V any](slice []V, expression func(index int, item V) bool) []V {
return FilterCopy(true, slice, expression)
}
// FilterFCopy 与 FilterCopy 的功能相同,但是 reserve 被默认为 false
func FilterFCopy[V any](slice []V, expression func(index int, item V) bool) []V {
return FilterCopy(false, slice, expression)
}

View File

@ -0,0 +1,34 @@
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)
}
}

17
utils/slice/group.go Normal file
View File

@ -0,0 +1,17 @@
package slice
// GroupBy 返回分组后的切片
func GroupBy[T any, K comparable](collection []T, fn func(T) K) map[K][]T {
if len(collection) == 0 {
return nil
}
result := make(map[K][]T, len(collection))
for _, item := range collection {
key := fn(item)
result[key] = append(result[key], item)
}
return result
}

14
utils/slice/map.go Normal file
View File

@ -0,0 +1,14 @@
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
}

28
utils/slice/reduce.go Normal file
View File

@ -0,0 +1,28 @@
package slice
import (
"github.com/kercylan98/minotaur/utils/generic"
)
// Reduce 将切片中的多个元素组合成一个单一值
// - start: 开始索引,如果为负数则从后往前计算,例如:-1 表示从最后一个元素开始向左遍历1 表示从第二个元素开始
// - slice: 待组合的切片
// - reducer: 组合函数
func Reduce[V any, R generic.Number](start int, slice []V, reducer func(index int, item V, current R) R) (result R) {
length := len(slice)
if start >= length || -start > length {
return
}
if start < 0 {
for i := length + start; i >= 0; i-- {
result = reducer(i, slice[i], result)
}
} else {
for i := start; i < length; i++ {
result = reducer(i, slice[i], result)
}
}
return result
}

View File

@ -0,0 +1,17 @@
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")
}
}

30
utils/slice/shuffle.go Normal file
View File

@ -0,0 +1,30 @@
package slice
import "math/rand"
// Shuffle 随机打乱切片
// - 该函数会改变传入的切片,如果不希望改变原切片,需要在函数调用之前手动复制一份或者使用 ShuffleCopy 函数
func Shuffle[T any](collection []T) []T {
rand.Shuffle(len(collection), func(i, j int) {
collection[i], collection[j] = collection[j], collection[i]
})
return collection
}
// ShuffleCopy 返回随机打乱后的切片
// - 该函数不会改变原切片
func ShuffleCopy[T any](collection []T) []T {
if len(collection) == 0 {
return nil
}
result := make([]T, len(collection))
perm := rand.Perm(len(collection))
for i, randIndex := range perm {
result[i] = collection[randIndex]
}
return result
}

View File

@ -1,10 +1,12 @@
package slice package slice
import ( import (
"math/rand"
"reflect" "reflect"
) )
// Slice 切片类型
type Slice[V any] []V
// GetValue 获取特定索引的元素,如果索引超出范围则返回零值 // GetValue 获取特定索引的元素,如果索引超出范围则返回零值
func GetValue[V any](slice []V, i int) (v V) { func GetValue[V any](slice []V, i int) (v V) {
if i >= 0 && i < len(slice) { if i >= 0 && i < len(slice) {
@ -108,14 +110,6 @@ func Reverse[V any](slice []V) {
} }
} }
// Shuffle 随机打乱数组
func Shuffle[V any](slice []V) {
for i := 0; i < len(slice); i++ {
j := rand.Intn(len(slice))
slice[i], slice[j] = slice[j], slice[i]
}
}
// Distinct 去重 // Distinct 去重
func Distinct[V any](slice []V) []V { func Distinct[V any](slice []V) []V {
var result []V var result []V
@ -333,25 +327,3 @@ func SubWithCheck[T any](a, b []T, checkHandle func(a, b T) bool) []T {
} }
return result return result
} }
// Filter 过滤切片中的元素
// - filterHandle 返回 true 表示需要保留
func Filter[T any](a []T, filterHandle func(a T) bool) []T {
var result []T
for _, a := range a {
if filterHandle(a) {
result = append(result, a)
}
}
return result
}
// Mapping 将切片中的元素进行转换
// - mappingHandle 返回转换后的元素
func Mapping[T any, R any](a []T, mappingHandle func(a T) R) []R {
var result []R
for _, a := range a {
result = append(result, mappingHandle(a))
}
return result
}

44
utils/slice/unique.go Normal file
View File

@ -0,0 +1,44 @@
package slice
// Unique 返回去重后的切片
func Unique[T comparable](collection []T) []T {
if len(collection) == 0 {
return nil
}
result := make([]T, 0, len(collection))
seen := make(map[T]struct{}, len(collection))
for _, item := range collection {
if _, ok := seen[item]; ok {
continue
}
seen[item] = struct{}{}
result = append(result, item)
}
return result
}
// UniqueBy 返回去重后的切片
func UniqueBy[T any](collection []T, fn func(T) any) []T {
if len(collection) == 0 {
return nil
}
result := make([]T, 0, len(collection))
seen := make(map[any]struct{}, len(collection))
for _, item := range collection {
key := fn(item)
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
result = append(result, item)
}
return result
}

11
utils/slice/zoom.go Normal file
View File

@ -0,0 +1,11 @@
package slice
// Zoom 将切片的长度缩放到指定的大小,如果 newSize 小于 slice 的长度,则会截断 slice如果 newSize 大于 slice 的长度,则会在 slice 的末尾添加零值数据
func Zoom[V any](newSize int, slice []V) []V {
if newSize < 0 {
newSize = 0
}
var s = make([]V, newSize)
copy(s, slice)
return s
}

View File

@ -1,185 +1,88 @@
package stream package stream
import ( import (
"github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/hash" "github.com/kercylan98/minotaur/utils/hash"
"reflect"
) )
// WithMap 使用传入的 map 执行链式操作
// - 该函数将会直接影响到传入的 map
func WithMap[K comparable, V any](m map[K]V) Map[K, V] {
return m
}
// WithMapCopy 使用传入的 map 执行链式操作
// - 该函数不会影响到传入的 map
func WithMapCopy[K comparable, V any](m map[K]V) Map[K, V] {
return hash.Copy(m)
}
// Map 提供了 map 的链式操作
type Map[K comparable, V any] map[K]V type Map[K comparable, V any] map[K]V
// Set 设置一个值 // Map 返回映射
func (slf Map[K, V]) Set(key K, value V) Map[K, V] { func (slf Map[K, V]) Map() map[K]V {
slf[key] = value
return slf return slf
} }
// Delete 删除一个值 // Keys 返回键列表
func (slf Map[K, V]) Delete(key K) Map[K, V] { func (slf Map[K, V]) Keys() Slice[K] {
delete(slf, key) var res = make([]K, 0, len(slf))
for key := range slf {
res = append(res, key)
}
return res
}
// Values 返回值列表
func (slf Map[K, V]) Values() Slice[V] {
var res = make([]V, 0, len(slf))
for _, value := range slf {
res = append(res, value)
}
return res
}
// Filter 是 hash.Filter 的快捷方式
func (slf Map[K, V]) Filter(reserve bool, expression func(key K, value V) bool) Map[K, V] {
return hash.Filter(reserve, slf, expression)
}
// FilterT 是 hash.FilterT 的快捷方式
func (slf Map[K, V]) FilterT(expression func(key K, value V) bool) Map[K, V] {
return hash.FilterT(slf, expression)
}
// FilterF 是 hash.FilterF 的快捷方式
func (slf Map[K, V]) FilterF(expression func(key K, value V) bool) Map[K, V] {
return hash.FilterF(slf, expression)
}
// FilterCopy 是 hash.FilterCopy 的快捷方式
func (slf Map[K, V]) FilterCopy(reserve bool, expression func(key K, value V) bool) Map[K, V] {
return hash.FilterCopy(reserve, slf, expression)
}
// FilterTCopy 是 hash.FilterTCopy 的快捷方式
func (slf Map[K, V]) FilterTCopy(expression func(key K, value V) bool) Map[K, V] {
return hash.FilterTCopy(slf, expression)
}
// FilterFCopy 是 hash.FilterFCopy 的快捷方式
func (slf Map[K, V]) FilterFCopy(expression func(key K, value V) bool) Map[K, V] {
return hash.FilterFCopy(slf, expression)
}
// Chunk 是 hash.Chunk 的快捷方式
func (slf Map[K, V]) Chunk(size int) Slice[Map[K, V]] {
chunks := hash.Chunk(slf, size)
var res = make([]Map[K, V], 0, len(chunks))
for i := range chunks {
res = append(res, chunks[i])
}
return res
}
// Each 是 hash.Each 的快捷方式
func (slf Map[K, V]) Each(abort bool, iterator func(i int, key K, item V) bool) Map[K, V] {
hash.Each(abort, slf, iterator)
return slf return slf
} }
// Filter 过滤 handle 返回 false 的元素 // EachT 是 hash.EachT 的快捷方式
func (slf Map[K, V]) Filter(handle func(key K, value V) bool) Map[K, V] { func (slf Map[K, V]) EachT(iterator func(i int, key K, item V) bool) Map[K, V] {
for k, v := range slf { hash.EachT(slf, iterator)
if !handle(k, v) {
delete(slf, k)
}
}
return slf return slf
} }
// FilterKey 过滤特定的 key // EachF 是 hash.EachF 的快捷方式
func (slf Map[K, V]) FilterKey(keys ...K) Map[K, V] { func (slf Map[K, V]) EachF(iterator func(i int, key K, item V) bool) Map[K, V] {
for _, key := range keys { hash.EachF(slf, iterator)
delete(slf, key)
}
return slf
}
// FilterValue 过滤特定的 value
func (slf Map[K, V]) FilterValue(values ...V) Map[K, V] {
for k, v := range slf {
for _, value := range values {
if reflect.DeepEqual(v, value) {
delete(slf, k)
}
}
}
return slf
}
// RandomKeep 随机保留 n 个元素
func (slf Map[K, V]) RandomKeep(n int) Map[K, V] {
length := len(slf)
if n >= length {
return slf
}
for k := range slf {
if n > 0 {
n--
} else {
delete(slf, k)
}
}
return slf
}
// RandomDelete 随机删除 n 个元素
func (slf Map[K, V]) RandomDelete(n int) Map[K, V] {
var count int
for k := range slf {
if count < n {
count++
delete(slf, k)
} else {
return slf
}
}
return slf
}
// RandomReplace 将 values 覆盖到当前的 map 中
// - 如果 values 的长度大于当前 map 的长度,则只会覆盖当前 map 的长度
func (slf Map[K, V]) RandomReplace(values ...V) Map[K, V] {
var record []K
var valuesLen = len(values)
for k := range slf {
record = append(record, k)
if len(record) >= valuesLen {
break
}
}
for i, k := range record {
slf.Set(k, values[i])
}
return slf
}
// Distinct 去重,如果 handle 返回 true则认为是重复的元素
func (slf Map[K, V]) Distinct(handle func(key K, value V) bool) Map[K, V] {
for k, v := range slf {
if handle(k, v) {
delete(slf, k)
}
}
return slf
}
// Range 遍历当前 Map, handle 返回 false 则停止遍历
func (slf Map[K, V]) Range(handle func(key K, value V) bool) Map[K, V] {
for k, v := range slf {
if !handle(k, v) {
break
}
}
return slf
}
// ValueOr 当 key 不存在时,设置一个默认值
func (slf Map[K, V]) ValueOr(key K, value V) Map[K, V] {
if _, ok := slf[key]; !ok {
slf[key] = value
}
return slf
}
// GetValueOr 当 key 不存在时,返回一个默认值
func (slf Map[K, V]) GetValueOr(key K, value V) V {
if v, ok := slf[key]; ok {
return v
}
return value
}
// Clear 清空当前 Map
func (slf Map[K, V]) Clear() Map[K, V] {
for k := range slf {
delete(slf, k)
}
return slf
}
// Merge 合并多个 Map
func (slf Map[K, V]) Merge(maps ...map[K]V) Map[K, V] {
for _, m := range maps {
for k, v := range m {
slf[k] = v
}
}
return slf
}
// ToSliceStream 将当前 Map stream 转换为 Slice stream
func (slf Map[K, V]) ToSliceStream() Slice[V] {
return hash.ToSlice(slf)
}
// ToSliceStreamWithKey 将当前 Map stream 转换为 Slice streamkey 为 Slice 的元素
func (slf Map[K, V]) ToSliceStreamWithKey() Slice[K] {
return hash.KeyToSlice(slf)
}
// ToSyncMap 将当前 Map 转换为 concurrent.BalanceMap
func (slf Map[K, V]) ToSyncMap() *concurrent.BalanceMap[K, V] {
return concurrent.NewBalanceMap[K, V](concurrent.WithBalanceMapSource(slf))
}
// ToMap 将当前 Map 转换为 map
func (slf Map[K, V]) ToMap() map[K]V {
return slf return slf
} }

View File

@ -1,26 +0,0 @@
package stream_test
import (
"fmt"
"github.com/kercylan98/minotaur/utils/stream"
)
func ExampleWithMap() {
m := stream.WithMap(map[int]string{1: "a", 2: "b", 3: "c", 4: "d", 5: "d"}).Filter(func(key int, value string) bool {
return key > 3
})
fmt.Println(len(m))
// Output:
// 2
}
func ExampleWithMapCopy() {
m := stream.WithMapCopy(map[int]string{1: "a", 2: "b", 3: "c", 4: "d", 5: "d"}).Filter(func(key int, value string) bool {
return key > 3
})
fmt.Println(len(m))
// Output:
// 2
}

View File

@ -1,90 +0,0 @@
package stream_test
import (
"github.com/kercylan98/minotaur/utils/stream"
. "github.com/smartystreets/goconvey/convey"
"testing"
)
func initMap() map[int]int {
return map[int]int{
1: 100,
2: 200,
3: 300,
}
}
func TestWithMap(t *testing.T) {
Convey("TestWithMap", t, func() {
var s = initMap()
var m = stream.WithMap(s).RandomDelete(1)
So(m, ShouldNotBeNil)
So(len(s), ShouldEqual, 2)
})
}
func TestWithMapCopy(t *testing.T) {
Convey("TestWithMapCopy", t, func() {
var s = initMap()
var m = stream.WithMapCopy(s).RandomDelete(1)
So(m, ShouldNotBeNil)
So(len(s), ShouldEqual, 3)
})
}
func TestMap_Set(t *testing.T) {
Convey("TestMap_Set", t, func() {
var m = stream.WithMap(initMap()).Set(4, 400)
So(m[4], ShouldEqual, 400)
})
}
func TestMap_Filter(t *testing.T) {
Convey("TestMap_Filter", t, func() {
var m = stream.WithMap(initMap()).Filter(func(key int, value int) bool {
return key == 1
})
So(len(m), ShouldEqual, 1)
})
}
func TestMap_FilterKey(t *testing.T) {
Convey("TestMap_FilterKey", t, func() {
var m = stream.WithMap(initMap()).FilterKey(1)
So(len(m), ShouldEqual, 2)
})
}
func TestMap_FilterValue(t *testing.T) {
Convey("TestMap_FilterValue", t, func() {
var m = stream.WithMap(initMap()).FilterValue(100)
So(len(m), ShouldEqual, 2)
})
}
func TestMap_RandomKeep(t *testing.T) {
Convey("TestMap_RandomKeep", t, func() {
var m = stream.WithMap(initMap()).RandomKeep(1)
So(len(m), ShouldEqual, 1)
})
}
func TestMap_RandomDelete(t *testing.T) {
Convey("TestMap_RandomDelete", t, func() {
var m = stream.WithMap(initMap()).RandomDelete(1)
So(len(m), ShouldEqual, 2)
})
}
func TestMap_Distinct(t *testing.T) {
Convey("TestMap_Distinct", t, func() {
var m = stream.WithMap(map[int]int{
1: 100,
2: 200,
3: 100,
}).Distinct(func(key int, value int) bool {
return value == 100
})
So(len(m), ShouldEqual, 1)
})
}

View File

@ -1,152 +1,179 @@
package stream package stream
import ( import "github.com/kercylan98/minotaur/utils/slice"
"github.com/kercylan98/minotaur/utils/hash"
"github.com/kercylan98/minotaur/utils/slice"
"reflect"
)
// WithSlice 创建一个 Slice
// - 该函数不会影响到传入的 slice
func WithSlice[V any](values []V) Slice[V] {
return slice.Copy(values)
}
// Slice 提供了 slice 的链式操作
type Slice[V any] []V type Slice[V any] []V
// Filter 过滤 handle 返回 false 的元素 // Slice 返回切片
func (slf Slice[V]) Filter(handle func(index int, value V) bool) Slice[V] { func (slf Slice[V]) Slice() []V {
var ns = make([]V, 0, len(slf))
for i, v := range slf {
if handle(i, v) {
ns = append(ns, v)
}
}
return ns
}
// FilterValue 过滤特定的 value
func (slf Slice[V]) FilterValue(values ...V) Slice[V] {
return slf.Filter(func(index int, value V) bool {
for _, v := range values {
if reflect.DeepEqual(v, value) {
return false
}
}
return true
})
}
// FilterIndex 过滤特定的 index
func (slf Slice[V]) FilterIndex(indexes ...int) Slice[V] {
return slf.Filter(func(index int, value V) bool {
return !slice.Contains(indexes, index)
})
}
// RandomKeep 随机保留 n 个元素
func (slf Slice[V]) RandomKeep(n int) Slice[V] {
length := len(slf)
if n >= length {
return slf return slf
} }
var hit = make([]int, length)
for i := 0; i < n; i++ { // Copy 复制一份切片
hit[i] = 1 func (slf Slice[V]) Copy() Slice[V] {
} return slice.Copy(slf)
slice.Shuffle(hit)
var ns = make([]V, 0, n)
for i, v := range slf {
if hit[i] == 1 {
ns = append(ns, v)
}
}
return ns
} }
// RandomDelete 随机删除 n 个元素 // Zoom 是 slice.Zoom 的快捷方式
func (slf Slice[V]) RandomDelete(n int) Slice[V] { func (slf Slice[V]) Zoom(newSize int) Slice[V] {
length := len(slf) return slice.Zoom(newSize, slf)
if n >= length {
return slf[:0]
}
var hit = make([]int, length)
for i := 0; i < n; i++ {
hit[i] = 1
}
slice.Shuffle(hit)
var ns = make([]V, 0, n)
for i, v := range slf {
if hit[i] == 0 {
ns = append(ns, v)
}
}
return ns
} }
// Shuffle 随机打乱 // Indexes 将切片转换为索引切片
func (slf Slice[V]) Indexes() Slice[int] {
var s = make([]int, len(slf))
for i := 0; i < len(s); i++ {
s[i] = i
}
return s
}
// Map 将切片转为 map
func (slf Slice[V]) Map() Map[int, V] {
var m = make(map[int]V, len(slf))
for k, v := range slf {
m[k] = v
}
return m
}
// Chunk 是 slice.Chunk 的快捷方式
func (slf Slice[V]) Chunk(size int) Slices[V] {
chunks := slice.Chunk(slf, size)
result := make(Slices[V], len(chunks))
for i, chunk := range chunks {
result[i] = chunk
}
return result
}
// Drop 是 slice.Drop 的快捷方式
func (slf Slice[V]) Drop(start, n int) Slice[V] {
return slice.Drop(start, n, slf)
}
// DropBy 是 slice.DropBy 的快捷方式
func (slf Slice[V]) DropBy(fn func(index int, value V) bool) Slice[V] {
return slice.DropBy(slf, fn)
}
// Each 是 slice.Each 的快捷方式
func (slf Slice[V]) Each(abort bool, iterator func(index int, item V) bool) Slice[V] {
slice.Each(abort, slf, iterator)
return slf
}
// EachT 是 slice.EachT 的快捷方式
func (slf Slice[V]) EachT(iterator func(index int, item V) bool) Slice[V] {
slice.EachT(slf, iterator)
return slf
}
// EachF 是 slice.EachF 的快捷方式
func (slf Slice[V]) EachF(iterator func(index int, item V) bool) Slice[V] {
slice.EachF(slf, iterator)
return slf
}
// EachReverse 是 slice.EachReverse 的快捷方式
func (slf Slice[V]) EachReverse(abort bool, iterator func(index int, item V) bool) Slice[V] {
slice.EachReverse(abort, slf, iterator)
return slf
}
// EachReverseT 是 slice.EachReverseT 的快捷方式
func (slf Slice[V]) EachReverseT(iterator func(index int, item V) bool) Slice[V] {
slice.EachReverseT(slf, iterator)
return slf
}
// EachReverseF 是 slice.EachReverseF 的快捷方式
func (slf Slice[V]) EachReverseF(iterator func(index int, item V) bool) Slice[V] {
slice.EachReverseF(slf, iterator)
return slf
}
// FillBy 是 slice.FillBy 的快捷方式
func (slf Slice[V]) FillBy(fn func(index int, value V) V) Slice[V] {
return slice.FillBy(slf, fn)
}
// FillByCopy 是 slice.FillByCopy 的快捷方式
func (slf Slice[V]) FillByCopy(fn func(index int, value V) V) Slice[V] {
return slice.FillByCopy(slf, fn)
}
// FillUntil 是 slice.FillUntil 的快捷方式
func (slf Slice[V]) FillUntil(abort bool, fn func(index int, value V) (V, bool)) Slice[V] {
return slice.FillUntil(abort, slf, fn)
}
// FillUntilCopy 是 slice.FillUntilCopy 的快捷方式
func (slf Slice[V]) FillUntilCopy(abort bool, fn func(index int, value V) (V, bool)) Slice[V] {
return slice.FillUntilCopy(abort, slf, fn)
}
// FillUntilT 是 slice.FillUntilT 的快捷方式
func (slf Slice[V]) FillUntilT(fn func(index int, value V) (V, bool)) Slice[V] {
return slice.FillUntilT(slf, fn)
}
// FillUntilF 是 slice.FillUntilF 的快捷方式
func (slf Slice[V]) FillUntilF(fn func(index int, value V) (V, bool)) Slice[V] {
return slice.FillUntilF(slf, fn)
}
// FillUntilTCopy 是 slice.FillUntilTCopy 的快捷方式
func (slf Slice[V]) FillUntilTCopy(fn func(index int, value V) (V, bool)) Slice[V] {
return slice.FillUntilTCopy(slf, fn)
}
// FillUntilFCopy 是 slice.FillUntilFCopy 的快捷方式
func (slf Slice[V]) FillUntilFCopy(fn func(index int, value V) (V, bool)) Slice[V] {
return slice.FillUntilFCopy(slf, fn)
}
// Filter 是 slice.Filter 的快捷方式
func (slf Slice[V]) Filter(reserve bool, expression func(index int, item V) bool) Slice[V] {
return slice.Filter(reserve, slf, expression)
}
// FilterT 是 slice.FilterT 的快捷方式
func (slf Slice[V]) FilterT(expression func(index int, item V) bool) Slice[V] {
return slice.FilterT(slf, expression)
}
// FilterF 是 slice.FilterF 的快捷方式
func (slf Slice[V]) FilterF(expression func(index int, item V) bool) Slice[V] {
return slice.FilterF(slf, expression)
}
// FilterCopy 是 slice.FilterCopy 的快捷方式
func (slf Slice[V]) FilterCopy(reserve bool, expression func(index int, item V) bool) Slice[V] {
return slice.FilterCopy(reserve, slf, expression)
}
// FilterTCopy 是 slice.FilterTCopy 的快捷方式
func (slf Slice[V]) FilterTCopy(expression func(index int, item V) bool) Slice[V] {
return slice.FilterTCopy(slf, expression)
}
// FilterFCopy 是 slice.FilterFCopy 的快捷方式
func (slf Slice[V]) FilterFCopy(expression func(index int, item V) bool) Slice[V] {
return slice.FilterFCopy(slf, expression)
}
// Shuffle 是 slice.Shuffle 的快捷方式
func (slf Slice[V]) Shuffle() Slice[V] { func (slf Slice[V]) Shuffle() Slice[V] {
slice.Shuffle(slf) return slice.Shuffle(slf)
return slf
} }
// Reverse 反转 // ShuffleCopy 是 slice.ShuffleCopy 的快捷方式
func (slf Slice[V]) Reverse() Slice[V] { func (slf Slice[V]) ShuffleCopy() Slice[V] {
slice.Reverse(slf) return slice.ShuffleCopy(slf)
return slf
} }
// Clear 清空 // UniqueBy 是 slice.UniqueBy 的快捷方式
func (slf Slice[V]) Clear() Slice[V] { func (slf Slice[V]) UniqueBy(fn func(V) any) Slice[V] {
return slf[:0] return slice.UniqueBy(slf, fn)
}
// Distinct 去重,如果 handle 返回 true 则认为是重复元素
func (slf Slice[V]) Distinct() Slice[V] {
return slice.Distinct(slf)
}
// Merge 合并
func (slf Slice[V]) Merge(values ...V) Slice[V] {
return append(slf, values...)
}
// GetStartPart 获取前 n 个元素
func (slf Slice[V]) GetStartPart(n int) Slice[V] {
return slf[:n]
}
// GetEndPart 获取后 n 个元素
func (slf Slice[V]) GetEndPart(n int) Slice[V] {
return slice.GetEndPart(slf, n)
}
// GetPart 获取指定区间的元素
func (slf Slice[V]) GetPart(start, end int) Slice[V] {
return slice.GetPart(slf, start, end)
}
// ContainsHandle 如果包含指定的元素则执行 handle
func (slf Slice[V]) ContainsHandle(value V, handle func(slice Slice[V]) Slice[V]) Slice[V] {
if slice.ContainsAny(slf, value) {
return handle(slf)
}
return slf
}
// Set 设置指定位置的元素
func (slf Slice[V]) Set(index int, value V) Slice[V] {
slf[index] = value
return slf
}
// Delete 删除指定位置的元素
func (slf Slice[V]) Delete(index int) Slice[V] {
return append(slf, slf[index+1:]...)
}
// ToMapStream 将当前的 Slice stream 转换为 Map stream
func (slf Slice[V]) ToMapStream() Map[int, V] {
return hash.ToMap(slf)
} }

View File

@ -1,18 +1,34 @@
package stream_test package stream_test
import ( import (
"fmt"
"github.com/kercylan98/minotaur/utils/stream" "github.com/kercylan98/minotaur/utils/stream"
. "github.com/smartystreets/goconvey/convey"
"testing" "testing"
) )
func TestSlice_Filter(t *testing.T) { func TestStream(t *testing.T) {
Convey("TestSlice_Filter", t, func() { var s = stream.Slice[int]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
d := []int{1, 2, 3, 4, 5} t.Log(s, s.
var s = stream.WithSlice(d).Reverse() Copy().
fmt.Println(s) Shuffle().
fmt.Println(d) Filter(true, func(index int, item int) bool {
So(len(s), ShouldEqual, 2) return item%2 == 0
}) }).
Zoom(20).
Each(true, func(index int, item int) bool {
t.Log(index, item)
return false
}).
Chunk(3).
EachT(func(index int, item stream.Slice[int]) bool {
t.Log(item)
return false
}).
Merge().
FillBy(func(index int, value int) int {
if value == 0 {
return 999
}
return value
}),
)
} }

145
utils/stream/slices.go Normal file
View File

@ -0,0 +1,145 @@
package stream
import "github.com/kercylan98/minotaur/utils/slice"
type Slices[V any] []Slice[V]
// Merge 合并为一个切片
func (slf Slices[V]) Merge() Slice[V] {
var s = make([]V, 0)
for _, stream := range slf {
s = append(s, stream...)
}
return s
}
// Drop 的快捷方式
func (slf Slices[V]) Drop(start, n int) Slices[V] {
return slice.Drop(start, n, slf)
}
// DropBy 的快捷方式
func (slf Slices[V]) DropBy(fn func(index int, value Slice[V]) bool) Slices[V] {
return slice.DropBy(slf, fn)
}
// Each 的快捷方式
func (slf Slices[V]) Each(abort bool, iterator func(index int, item Slice[V]) bool) Slices[V] {
slice.Each(abort, slf, iterator)
return slf
}
// EachT 的快捷方式
func (slf Slices[V]) EachT(iterator func(index int, item Slice[V]) bool) Slices[V] {
slice.EachT(slf, iterator)
return slf
}
// EachF 的快捷方式
func (slf Slices[V]) EachF(iterator func(index int, item Slice[V]) bool) Slices[V] {
slice.EachF(slf, iterator)
return slf
}
// EachReverse 的快捷方式
func (slf Slices[V]) EachReverse(abort bool, iterator func(index int, item Slice[V]) bool) Slices[V] {
slice.EachReverse(abort, slf, iterator)
return slf
}
// EachReverseT 的快捷方式
func (slf Slices[V]) EachReverseT(iterator func(index int, item Slice[V]) bool) Slices[V] {
slice.EachReverseT(slf, iterator)
return slf
}
// EachReverseF 的快捷方式
func (slf Slices[V]) EachReverseF(iterator func(index int, item Slice[V]) bool) Slices[V] {
slice.EachReverseF(slf, iterator)
return slf
}
// FillBy 的快捷方式
func (slf Slices[V]) FillBy(fn func(index int, value Slice[V]) Slice[V]) Slices[V] {
return slice.FillBy(slf, fn)
}
// FillByCopy 的快捷方式
func (slf Slices[V]) FillByCopy(fn func(index int, value Slice[V]) Slice[V]) Slices[V] {
return slice.FillByCopy(slf, fn)
}
// FillUntil 的快捷方式
func (slf Slices[V]) FillUntil(abort bool, fn func(index int, value Slice[V]) (Slice[V], bool)) Slices[V] {
return slice.FillUntil(abort, slf, fn)
}
// FillUntilCopy 的快捷方式
func (slf Slices[V]) FillUntilCopy(abort bool, fn func(index int, value Slice[V]) (Slice[V], bool)) Slices[V] {
return slice.FillUntilCopy(abort, slf, fn)
}
// FillUntilT 的快捷方式
func (slf Slices[V]) FillUntilT(fn func(index int, value Slice[V]) (Slice[V], bool)) Slices[V] {
return slice.FillUntilT(slf, fn)
}
// FillUntilF 的快捷方式
func (slf Slices[V]) FillUntilF(fn func(index int, value Slice[V]) (Slice[V], bool)) Slices[V] {
return slice.FillUntilF(slf, fn)
}
// FillUntilTCopy 的快捷方式
func (slf Slices[V]) FillUntilTCopy(fn func(index int, value Slice[V]) (Slice[V], bool)) Slices[V] {
return slice.FillUntilTCopy(slf, fn)
}
// FillUntilFCopy 的快捷方式
func (slf Slices[V]) FillUntilFCopy(fn func(index int, value Slice[V]) (Slice[V], bool)) Slices[V] {
return slice.FillUntilFCopy(slf, fn)
}
// Filter 的快捷方式
func (slf Slices[V]) Filter(reserve bool, expression func(index int, item Slice[V]) bool) Slices[V] {
return slice.Filter(reserve, slf, expression)
}
// FilterT 的快捷方式
func (slf Slices[V]) FilterT(expression func(index int, item Slice[V]) bool) Slices[V] {
return slice.FilterT(slf, expression)
}
// FilterF 的快捷方式
func (slf Slices[V]) FilterF(expression func(index int, item Slice[V]) bool) Slices[V] {
return slice.FilterF(slf, expression)
}
// FilterCopy 的快捷方式
func (slf Slices[V]) FilterCopy(reserve bool, expression func(index int, item Slice[V]) bool) Slices[V] {
return slice.FilterCopy(reserve, slf, expression)
}
// FilterTCopy 的快捷方式
func (slf Slices[V]) FilterTCopy(expression func(index int, item Slice[V]) bool) Slices[V] {
return slice.FilterTCopy(slf, expression)
}
// FilterFCopy 的快捷方式
func (slf Slices[V]) FilterFCopy(expression func(index int, item Slice[V]) bool) Slices[V] {
return slice.FilterFCopy(slf, expression)
}
// Shuffle 的快捷方式
func (slf Slices[V]) Shuffle() Slices[V] {
return slice.Shuffle(slf)
}
// ShuffleCopy 的快捷方式
func (slf Slices[V]) ShuffleCopy() Slices[V] {
return slice.ShuffleCopy(slf)
}
// UniqueBy 的快捷方式
func (slf Slices[V]) UniqueBy(fn func(Slice[V]) any) Slices[V] {
return slice.UniqueBy(slf, fn)
}