Merge branch 'develop'
This commit is contained in:
commit
854ee9adf8
|
@ -1,7 +1,7 @@
|
||||||
package task
|
package task
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StateAccept State = iota // 已接受
|
StateAccept State = iota + 1 // 已接受
|
||||||
StateFinish // 已完成
|
StateFinish // 已完成
|
||||||
StateReward // 已领取
|
StateReward // 已领取
|
||||||
StateFail // 已失败
|
StateFail // 已失败
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 返回消息类型的字符串表示
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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 stream,key 为 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
Loading…
Reference in New Issue