From 54262a7525099adc26fb68adade5a9274958a476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 25 Nov 2025 11:33:30 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=95=B0?= =?UTF-8?q?=E7=BB=84=E8=BD=ACMap=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- op_array/util.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 op_array/util.go diff --git a/op_array/util.go b/op_array/util.go new file mode 100644 index 0000000..5c7cf04 --- /dev/null +++ b/op_array/util.go @@ -0,0 +1,18 @@ +// Package op_array ... +// +// Description : op_array ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2025-11-25 11:30 +package op_array + +// ToMap 数组转map +func ToMap[Key comparable, Value any](dataList []Value, keyFormat func(item Value) Key) map[Key]Value { + res := make(map[Key]Value) + for _, item := range dataList { + key := keyFormat(item) + res[key] = item + } + return res +} From d3eb702c7449a52ee7097e326e4d2f5a76d264c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 25 Nov 2025 11:36:20 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=8F=90?= =?UTF-8?q?=E5=8F=96=E6=95=B0=E7=BB=84=E6=8C=87=E5=AE=9A=E5=AD=97=E6=AE=B5?= =?UTF-8?q?,=20=E5=B9=B6=E7=94=9F=E6=88=90=E6=96=B0=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- op_array/util.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/op_array/util.go b/op_array/util.go index 5c7cf04..9d229a0 100644 --- a/op_array/util.go +++ b/op_array/util.go @@ -16,3 +16,13 @@ func ToMap[Key comparable, Value any](dataList []Value, keyFormat func(item Valu } return res } + +// ExtractField 提取数组指定字段, 并构建成一个新的数组 +func ExtractField[FieldValue any, Value any](dataList []Value, fieldValue func(item Value) FieldValue) []FieldValue { + res := make([]FieldValue, 0) + for _, item := range dataList { + v := fieldValue(item) + res = append(res, v) + } + return res +} From 2cbffbd8832a5d64a87dfc2ae404d64e48ce609a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 25 Nov 2025 11:55:58 +0800 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=E8=BF=87=E6=BB=A4=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=95=B0=E6=8D=AE=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- op_array/util.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/op_array/util.go b/op_array/util.go index 9d229a0..29854f6 100644 --- a/op_array/util.go +++ b/op_array/util.go @@ -26,3 +26,14 @@ func ExtractField[FieldValue any, Value any](dataList []Value, fieldValue func(i } return res } + +// Filter 过滤列表数据 +func Filter[Value any](dataList []Value, filterValue func(item Value) bool) []Value { + res := make([]Value, 0) + for _, item := range dataList { + if filterValue(item) { + res = append(res, item) + } + } + return res +} From 38ff57668268c5e9a9192ea990fbbc4fce46cabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 26 Nov 2025 12:48:12 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=E8=87=AA=E5=8A=A8=E4=BB=A5value=E5=80=BC=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E7=9A=84=E8=BD=AC=E6=8D=A2=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- op_array/util.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/op_array/util.go b/op_array/util.go index 29854f6..9efa97e 100644 --- a/op_array/util.go +++ b/op_array/util.go @@ -17,6 +17,16 @@ func ToMap[Key comparable, Value any](dataList []Value, keyFormat func(item Valu return res } +// ToCustomMap 数组转map +func ToCustomMap[Key comparable, Value any, CustomValue any](dataList []Value, keyFormat func(item Value) Key, formatCustomValue func(item Value) CustomValue) map[Key]CustomValue { + res := make(map[Key]CustomValue) + for _, item := range dataList { + key := keyFormat(item) + res[key] = formatCustomValue(item) + } + return res +} + // ExtractField 提取数组指定字段, 并构建成一个新的数组 func ExtractField[FieldValue any, Value any](dataList []Value, fieldValue func(item Value) FieldValue) []FieldValue { res := make([]FieldValue, 0) From 98f7414d9e49d08ca488e928f7b60460d317f1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 26 Nov 2025 21:00:36 +0800 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=95=B0?= =?UTF-8?q?=E7=BB=84=E5=88=86=E7=BB=84=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- op_array/util.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/op_array/util.go b/op_array/util.go index 9efa97e..118abd1 100644 --- a/op_array/util.go +++ b/op_array/util.go @@ -7,6 +7,8 @@ // Date : 2025-11-25 11:30 package op_array +import "fmt" + // ToMap 数组转map func ToMap[Key comparable, Value any](dataList []Value, keyFormat func(item Value) Key) map[Key]Value { res := make(map[Key]Value) @@ -47,3 +49,22 @@ func Filter[Value any](dataList []Value, filterValue func(item Value) bool) []Va } return res } + +// Group 按照指定字段进行分组 +func Group[Key comparable, Value any](dataList []Value, keyFormat func(item Value) Key) [][]Value { + keyList := make([]string, 0) + dataTable := make(map[string][]Value) + res := make([][]Value, 0) + for _, item := range dataList { + key := fmt.Sprintf("%v", keyFormat(item)) + if _, ok := dataTable[key]; !ok { + keyList = append(keyList, key) + } + dataTable[key] = append(dataTable[key], item) + } + formatList := make([][]Value, 0) + for _, key := range keyList { + formatList = append(formatList, dataTable[key]) + } + return res +} From b0d0a6db285ec1638ff91720354a0ad1772eb61b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 27 Nov 2025 12:44:06 +0800 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=B0=86?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=95=B0=E6=8D=AE=E8=BD=AC=E6=8D=A2=E4=B8=BA?= =?UTF-8?q?=E6=A0=91=E5=BD=A2=E7=BB=93=E6=9E=84=E7=9A=84=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- op_array/util.go | 46 ++++++++++++++++++++++++++++++++++++++++++- op_array/util_test.go | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 op_array/util_test.go diff --git a/op_array/util.go b/op_array/util.go index 118abd1..e7c1da7 100644 --- a/op_array/util.go +++ b/op_array/util.go @@ -7,7 +7,9 @@ // Date : 2025-11-25 11:30 package op_array -import "fmt" +import ( + "fmt" +) // ToMap 数组转map func ToMap[Key comparable, Value any](dataList []Value, keyFormat func(item Value) Key) map[Key]Value { @@ -68,3 +70,45 @@ func Group[Key comparable, Value any](dataList []Value, keyFormat func(item Valu } return res } + +// TreeItem 数据结构 +type TreeItem[ID comparable, Value any] struct { + ID ID `json:"id" dc:"数据ID"` + ParentID ID `json:"parent_id" dc:"父级ID"` + Value Value `json:"value" dc:"数据值"` + ChildList []*TreeItem[ID, Value] `json:"child_list" dc:"子级数据列表"` +} + +// Tree 将列表数据转为属性结构 +func Tree[ID comparable, Value any](dataList []Value, formatParentID func(v Value) ID, formatID func(v Value) ID) *TreeItem[ID, Value] { + // list 转table + dataTable := make(map[ID]*TreeItem[ID, Value]) + for _, item := range dataList { + dataID := formatID(item) + dataTable[dataID] = &TreeItem[ID, Value]{ + ID: dataID, + ParentID: formatParentID(item), + Value: item, + ChildList: make([]*TreeItem[ID, Value], 0), + } + } + // 构建树(保证一定是一棵树) + rootData := &TreeItem[ID, Value]{ + ID: *new(ID), + ParentID: *new(ID), + Value: *new(Value), + ChildList: make([]*TreeItem[ID, Value], 0), // 子数据列表 + } + for _, item := range dataList { + dataID := formatID(item) + parentID := formatParentID(item) + if _, exist := dataTable[parentID]; exist { + // 归属某一个父级节点 + dataTable[parentID].ChildList = append(dataTable[parentID].ChildList, dataTable[dataID]) + } else { + // 部署于任何父节点, 则归属于虚拟的根节点 + rootData.ChildList = append(rootData.ChildList, dataTable[dataID]) + } + } + return rootData +} diff --git a/op_array/util_test.go b/op_array/util_test.go new file mode 100644 index 0000000..7c057cf --- /dev/null +++ b/op_array/util_test.go @@ -0,0 +1,34 @@ +// Package op_array ... +// +// Description : op_array ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2025-11-27 12:40 +package op_array + +import ( + "testing" + + "git.zhangdeman.cn/zhangdeman/serialize" +) + +func TestTree(t *testing.T) { + type Data struct { + ID int `json:"id" dc:"数据ID"` + ParentID int `json:"parent_id" dc:"父级ID"` + Value string `json:"value" dc:"数据值"` + } + + dataList := []Data{ + {ID: 1, ParentID: 0, Value: "1"}, + {ID: 2, ParentID: 1, Value: "2"}, + {ID: 3, ParentID: 1, Value: "3"}, + {ID: 4, ParentID: 2, Value: "4"}, + {ID: 5, ParentID: 2, Value: "5"}, + {ID: 6, ParentID: 3, Value: "6"}, + {ID: 7, ParentID: 3, Value: "7"}, + } + res := Tree(dataList, func(v Data) int { return v.ParentID }, func(v Data) int { return v.ID }) + serialize.JSON.ConsoleOutput(res) +} From 30c1d54f49e943ab73451eb866eebb8747f4af06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 27 Nov 2025 14:50:27 +0800 Subject: [PATCH 7/8] =?UTF-8?q?feat:=20=E6=B7=B1=E5=BA=A6=E5=85=8B?= =?UTF-8?q?=E9=9A=86=E6=94=AF=E6=8C=81map=E5=A4=8D=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 1 + go.sum | 3 +++ op_array/util.go | 32 ++++++++++++++++++++++++++++++++ op_array/util_test.go | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/go.mod b/go.mod index 34a1761..32b8bf4 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/smarty/assertions v1.15.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/sjson v1.2.5 // indirect golang.org/x/mod v0.9.0 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/tools v0.7.0 // indirect diff --git a/go.sum b/go.sum index 31ccf8c..07c7879 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,7 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= @@ -52,6 +53,8 @@ github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= diff --git a/op_array/util.go b/op_array/util.go index e7c1da7..5eec209 100644 --- a/op_array/util.go +++ b/op_array/util.go @@ -9,6 +9,7 @@ package op_array import ( "fmt" + "reflect" ) // ToMap 数组转map @@ -112,3 +113,34 @@ func Tree[ID comparable, Value any](dataList []Value, formatParentID func(v Valu } return rootData } + +// DeepClone 深度克隆一个数组, 每一个元素的地址均重新分配 +func DeepClone[Value any](dataList []Value) []Value { + v := *new(Value) + vType := reflect.TypeOf(v) + res := make([]Value, len(dataList)) + for i := 0; i < len(dataList); i++ { + newV := new(Value) + switch vType.Kind() { + case reflect.Ptr: + // 解引用, 并单独读取值后重新分配地址 + copyV := reflect.New(vType.Elem()).Elem() + copyV.Set(reflect.ValueOf(dataList[i]).Elem()) + *newV = copyV.Addr().Interface().(Value) + case reflect.Map: + mapVal := reflect.ValueOf(dataList[i]) + mapKeyList := mapVal.MapKeys() + newMap := reflect.MakeMap(vType) + for _, key := range mapKeyList { + // 通过键获取对应的值 + value := mapVal.MapIndex(key) + newMap.SetMapIndex(key, value) + } + *newV = newMap.Interface().(Value) + default: + *newV = dataList[i] + } + res[i] = *newV + } + return res +} diff --git a/op_array/util_test.go b/op_array/util_test.go index 7c057cf..d1848d0 100644 --- a/op_array/util_test.go +++ b/op_array/util_test.go @@ -8,6 +8,7 @@ package op_array import ( + "fmt" "testing" "git.zhangdeman.cn/zhangdeman/serialize" @@ -32,3 +33,38 @@ func TestTree(t *testing.T) { res := Tree(dataList, func(v Data) int { return v.ParentID }, func(v Data) int { return v.ID }) serialize.JSON.ConsoleOutput(res) } + +func TestClone(t *testing.T) { + type Data struct { + ID int `json:"id" dc:"数据ID"` + } + + // 字面结构体 + dataList := []Data{ + {ID: 1}, + {ID: 2}, + } + res := DeepClone(dataList) + fmt.Println(fmt.Sprintf("%p", &res[0]), fmt.Sprintf("%p", &dataList[0])) + fmt.Println(fmt.Sprintf("%p", &res[1]), fmt.Sprintf("%p", &dataList[1])) + + // 结构体指针 + dataList1 := []*Data{ + {ID: 1}, + {ID: 2}, + } + res1 := DeepClone(dataList1) + fmt.Println(fmt.Sprintf("%p", &res1[0]), fmt.Sprintf("%p", &dataList1[0])) + fmt.Println(fmt.Sprintf("%p", &res1[1]), fmt.Sprintf("%p", &dataList1[1])) + + // Map数据 + dataList2 := []map[string]any{ + {"id": 1}, + {"id": 2}, + } + res2 := DeepClone(dataList2) + fmt.Println(fmt.Sprintf("%p", &res2[0]), fmt.Sprintf("%p", &dataList2[0])) + fmt.Println(fmt.Sprintf("%p", &res2[1]), fmt.Sprintf("%p", &dataList2[1])) + serialize.JSON.ConsoleOutput(res2) + serialize.JSON.ConsoleOutput(dataList2) +} From efdd850e77fe32261038532800e18f6164e45fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 27 Nov 2025 15:18:42 +0800 Subject: [PATCH 8/8] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=BB=B6?= =?UTF-8?q?=E6=95=B0=E7=BB=84=E6=95=B0=E6=8D=AE=E6=8C=89=E7=85=A7=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E6=95=B0=E9=87=8F=E8=BF=9B=E8=A1=8C=E5=88=86=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- op_array/util.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/op_array/util.go b/op_array/util.go index 5eec209..2c3627b 100644 --- a/op_array/util.go +++ b/op_array/util.go @@ -144,3 +144,16 @@ func DeepClone[Value any](dataList []Value) []Value { } return res } + +// Shard 将数组按照指定数量进行分片 +func Shard[Value any](dataList []Value, itemShardCount int) [][]Value { + res := make([][]Value, 0) + for i := 0; i < len(dataList); i += itemShardCount { + tmpList := make([]Value, 0) + for j := 0; j < itemShardCount; j++ { + tmpList = append(tmpList, dataList[i+j]) + } + res = append(res, tmpList) + } + return res +}