+ ArrayTree
This commit is contained in:
parent
483deab4c3
commit
e6ada51cf7
3 changed files with 160 additions and 122 deletions
|
|
@ -14,6 +14,14 @@ type ArrayPluckValue struct {
|
|||
Items []map[string]interface{} `json:"items"`
|
||||
}
|
||||
|
||||
// ArrayTreeOption Array转树形结构参数表
|
||||
type ArrayTreeOption struct {
|
||||
Key string `json:"id"` // 主键名称, 默认为 id
|
||||
Empty interface{} `json:"empty"` // Top节点 parent 数值, 默认为 0
|
||||
Parent string `json:"parent"` // 父节点字段名称, 默认为 parent
|
||||
Children string `json:"children"` // 子节点字段名称, 默认为 children
|
||||
}
|
||||
|
||||
// ArrayColumn 返回多条数据记录,指定字段数值。
|
||||
func ArrayColumn(records []map[string]interface{}, name string) []interface{} {
|
||||
values := []interface{}{}
|
||||
|
|
@ -119,3 +127,69 @@ func OfArrayPluckValue(any interface{}) ArrayPluckValue {
|
|||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// NewArrayTreeOption 创建配置
|
||||
func NewArrayTreeOption(option map[string]interface{}) ArrayTreeOption {
|
||||
new := ArrayTreeOption{
|
||||
Empty: 0,
|
||||
Key: "id",
|
||||
Parent: "parent",
|
||||
Children: "children",
|
||||
}
|
||||
if v, ok := option["parent"].(string); ok {
|
||||
new.Parent = v
|
||||
}
|
||||
if v, ok := option["children"].(string); ok {
|
||||
new.Children = v
|
||||
}
|
||||
return new
|
||||
}
|
||||
|
||||
// ArrayTree []map[string]interface{} 转树形结构
|
||||
func ArrayTree(records []map[string]interface{}, setting map[string]interface{}) []map[string]interface{} {
|
||||
opt := NewArrayTreeOption(setting)
|
||||
return opt.Tree(records)
|
||||
}
|
||||
|
||||
// Tree Array 转换为 Tree
|
||||
func (opt ArrayTreeOption) Tree(records []map[string]interface{}) []map[string]interface{} {
|
||||
|
||||
mapping := map[string]map[string]interface{}{}
|
||||
for i := range records {
|
||||
if key, has := records[i][opt.Key]; has {
|
||||
records[i][opt.Children] = []map[string]interface{}{}
|
||||
mapping[fmt.Sprintf("%v", key)] = records[i]
|
||||
}
|
||||
}
|
||||
|
||||
// 向上归集
|
||||
for key, record := range mapping {
|
||||
parent := record[opt.Parent]
|
||||
if parent == opt.Empty { // 第一级
|
||||
continue
|
||||
}
|
||||
pKey := fmt.Sprintf("%v", parent)
|
||||
if _, has := mapping[pKey]; !has {
|
||||
continue
|
||||
}
|
||||
children, ok := mapping[pKey][opt.Children].([]map[string]interface{})
|
||||
if !ok {
|
||||
children = []map[string]interface{}{}
|
||||
}
|
||||
children = append(children, mapping[key])
|
||||
mapping[pKey][opt.Children] = children
|
||||
}
|
||||
|
||||
res := []map[string]interface{}{}
|
||||
for i := range records {
|
||||
if key, has := records[i][opt.Key]; has {
|
||||
record := mapping[fmt.Sprintf("%v", key)]
|
||||
parent := record[opt.Parent]
|
||||
if parent == opt.Empty { // 只保留第一级
|
||||
res = append(res, record)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,72 @@ import (
|
|||
"github.com/yaoapp/kun/maps"
|
||||
)
|
||||
|
||||
var testRecords = []map[string]interface{}{
|
||||
{"id": 1, "name": "云服务", "category_id": nil, "type_id": nil, "rank": 1, "parent_id": 0},
|
||||
{"id": 2, "name": "基础服务", "category_id": nil, "type_id": nil, "rank": 1, "parent_id": 1},
|
||||
{"id": 3, "name": "云主机", "category_id": nil, "type_id": 4, "rank": 1, "parent_id": 2},
|
||||
{"id": 4, "name": "对象存储", "category_id": nil, "type_id": 5, "rank": 2, "parent_id": 2},
|
||||
{"id": 5, "name": "云数据库", "category_id": nil, "type_id": 6, "rank": 3, "parent_id": 2},
|
||||
{"id": 6, "name": "块存储", "category_id": nil, "type_id": 7, "rank": 4, "parent_id": 2},
|
||||
{"id": 7, "name": "应用托管容器", "category_id": nil, "type_id": 8, "rank": 5, "parent_id": 2},
|
||||
{"id": 8, "name": "云缓存", "category_id": nil, "type_id": 9, "rank": 6, "parent_id": 2},
|
||||
{"id": 9, "name": "本地负载均衡", "category_id": nil, "type_id": 10, "rank": 7, "parent_id": 2},
|
||||
{"id": 10, "name": "全局负载均衡", "category_id": nil, "type_id": 13, "rank": 8, "parent_id": 2},
|
||||
{"id": 11, "name": "云分发", "category_id": nil, "type_id": 11, "rank": 9, "parent_id": 2},
|
||||
{"id": 12, "name": "企业级SaaS", "category_id": nil, "type_id": 12, "rank": 10, "parent_id": 2},
|
||||
{"id": 13, "name": "云桌面", "category_id": nil, "type_id": 14, "rank": 11, "parent_id": 2},
|
||||
{"id": 14, "name": "云备份", "category_id": nil, "type_id": 17, "rank": 12, "parent_id": 2},
|
||||
{"id": 15, "name": "GPU云主机", "category_id": nil, "type_id": 18, "rank": 13, "parent_id": 2},
|
||||
{"id": 16, "name": "物理云主机", "category_id": nil, "type_id": 20, "rank": 14, "parent_id": 2},
|
||||
{"id": 17, "name": "智能云", "category_id": 41, "type_id": nil, "rank": 2, "parent_id": 2},
|
||||
{"id": 18, "name": "软件和开发", "category_id": nil, "type_id": nil, "rank": 2, "parent_id": 0},
|
||||
{"id": 19, "name": "虚拟化及管理", "category_id": 59, "type_id": nil, "rank": 1, "parent_id": 18},
|
||||
{"id": 20, "name": "容器解决方案", "category_id": 65, "type_id": nil, "rank": 2, "parent_id": 18},
|
||||
{"id": 21, "name": "微服务解决方案", "category_id": 76, "type_id": nil, "rank": 3, "parent_id": 18},
|
||||
{"id": 22, "name": "serverless解决方案", "category_id": 82, "type_id": nil, "rank": 4, "parent_id": 18},
|
||||
{"id": 23, "name": "云管理和云运营", "category_id": nil, "type_id": nil, "rank": 3, "parent_id": 0},
|
||||
{"id": 24, "name": "混合云", "category_id": nil, "type_id": nil, "rank": 1, "parent_id": 23},
|
||||
{"id": 25, "name": "混合云解决方案", "category_id": 88, "type_id": nil, "rank": 1, "parent_id": 24},
|
||||
{"id": 26, "name": "混合云安全", "category_id": 671, "type_id": nil, "rank": 2, "parent_id": 24},
|
||||
{"id": 27, "name": "多云管理", "category_id": 94, "type_id": nil, "rank": 2, "parent_id": 23},
|
||||
{"id": 28, "name": "金牌运维", "category_id": 120, "type_id": nil, "rank": 3, "parent_id": 23},
|
||||
{"id": 29, "name": "研发运营一体化", "category_id": 443, "type_id": nil, "rank": 4, "parent_id": 23},
|
||||
{"id": 30, "name": "MSP", "category_id": 112, "type_id": nil, "rank": 5, "parent_id": 23},
|
||||
{"id": 31, "name": "安全与保险", "category_id": nil, "type_id": nil, "rank": 4, "parent_id": 0},
|
||||
{"id": 32, "name": "风险管理", "category_id": 141, "type_id": nil, "rank": 1, "parent_id": 31},
|
||||
{"id": 33, "name": "云服务用户数据保护", "category_id": 152, "type_id": nil, "rank": 2, "parent_id": 31},
|
||||
{"id": 34, "name": "业务风控", "category_id": nil, "type_id": nil, "rank": 3, "parent_id": 31},
|
||||
{"id": 35, "name": "内容安全", "category_id": 159, "type_id": nil, "rank": 1, "parent_id": 34},
|
||||
{"id": 36, "name": "反交易欺诈", "category_id": 164, "type_id": nil, "rank": 2, "parent_id": 34},
|
||||
{"id": 37, "name": "反信贷欺诈", "category_id": 165, "type_id": nil, "rank": 3, "parent_id": 34},
|
||||
{"id": 38, "name": "反营销欺诈", "category_id": 166, "type_id": nil, "rank": 4, "parent_id": 34},
|
||||
{"id": 39, "name": "反钓鱼欺诈", "category_id": 167, "type_id": nil, "rank": 5, "parent_id": 34},
|
||||
{"id": 40, "name": "云主机安全", "category_id": 184, "type_id": nil, "rank": 4, "parent_id": 31},
|
||||
{"id": 41, "name": "态势感知", "category_id": 190, "type_id": nil, "rank": 5, "parent_id": 31},
|
||||
{"id": 42, "name": "云保险", "category_id": 272, "type_id": nil, "rank": 6, "parent_id": 31},
|
||||
{"id": 43, "name": "云网&云边", "category_id": nil, "type_id": nil, "rank": 5, "parent_id": 0},
|
||||
{"id": 44, "name": "云平台网络能力", "category_id": 102, "type_id": nil, "rank": 1, "parent_id": 43},
|
||||
{"id": 45, "name": "SD-WAN", "category_id": 106, "type_id": nil, "rank": 2, "parent_id": 43},
|
||||
{"id": 46, "name": "物联网", "category_id": 234, "type_id": nil, "rank": 3, "parent_id": 43},
|
||||
{"id": 47, "name": "行业云", "category_id": nil, "type_id": nil, "rank": 6, "parent_id": 0},
|
||||
{"id": 48, "name": "政务", "category_id": nil, "type_id": nil, "rank": 1, "parent_id": 47},
|
||||
{"id": 49, "name": "政务云综合水平评估", "category_id": 215, "type_id": nil, "rank": 1, "parent_id": 48},
|
||||
{"id": 50, "name": "可信政务云评估", "category_id": 216, "type_id": nil, "rank": 2, "parent_id": 48},
|
||||
{"id": 51, "name": "金融", "category_id": 225, "type_id": nil, "rank": 2, "parent_id": 47},
|
||||
{"id": 52, "name": "开源治理", "category_id": nil, "type_id": nil, "rank": 7, "parent_id": 0},
|
||||
{"id": 53, "name": "面向开源用户企业", "category_id": 196, "type_id": nil, "rank": 1, "parent_id": 52},
|
||||
{"id": 54, "name": "面向自发开源企业", "category_id": 202, "type_id": nil, "rank": 2, "parent_id": 52},
|
||||
{"id": 55, "name": "开源项目评估", "category_id": 711, "type_id": nil, "rank": 3, "parent_id": 52},
|
||||
{"id": 56, "name": "开源工具评估", "category_id": 720, "type_id": nil, "rank": 4, "parent_id": 52},
|
||||
{"id": 57, "name": "检测平台", "category_id": nil, "type_id": nil, "rank": 8, "parent_id": 0},
|
||||
{"id": 58, "name": "云主机分级", "category_id": 371, "type_id": nil, "rank": 1, "parent_id": 57},
|
||||
{"id": 59, "name": "监管支撑", "category_id": nil, "type_id": nil, "rank": 9, "parent_id": 0},
|
||||
{"id": 60, "name": "企业上云效果成熟度", "category_id": 250, "type_id": nil, "rank": 1, "parent_id": 59},
|
||||
{"id": 61, "name": "综合信用评估", "category_id": nil, "type_id": nil, "rank": 2, "parent_id": 59},
|
||||
{"id": 62, "name": "云服务企业", "category_id": 261, "type_id": nil, "rank": 1, "parent_id": 61},
|
||||
{"id": 63, "name": "CDN服务企业", "category_id": 271, "type_id": nil, "rank": 2, "parent_id": 61},
|
||||
}
|
||||
|
||||
func TestArrayPluck(t *testing.T) {
|
||||
columns := []string{"城市", "行业", "计费"}
|
||||
pluck := map[string]interface{}{
|
||||
|
|
@ -34,3 +100,9 @@ func TestArraySplit(t *testing.T) {
|
|||
assert.Equal(t, 2, len(value))
|
||||
}
|
||||
}
|
||||
|
||||
func TestArrayTree(t *testing.T) {
|
||||
records := testRecords
|
||||
res := ArrayTree(records, map[string]interface{}{"parent": "parent_id"})
|
||||
assert.Equal(t, 9, len(res))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/yaoapp/gou"
|
||||
"github.com/yaoapp/gou/query/share"
|
||||
"github.com/yaoapp/kun/exception"
|
||||
"github.com/yaoapp/kun/utils"
|
||||
)
|
||||
|
||||
|
|
@ -16,6 +11,7 @@ func init() {
|
|||
gou.RegisterProcessHandler("xiang.helper.ArraySplit", ProcessArraySplit)
|
||||
gou.RegisterProcessHandler("xiang.helper.ArrayColumn", ProcessArrayColumn)
|
||||
gou.RegisterProcessHandler("xiang.helper.ArrayKeep", ProcessArrayKeep)
|
||||
gou.RegisterProcessHandler("xiang.helper.ArrayTree", ProcessArrayTree)
|
||||
gou.RegisterProcessHandler("xiang.helper.MapKeys", ProcessMapKeys)
|
||||
gou.RegisterProcessHandler("xiang.helper.MapValues", ProcessMapValues)
|
||||
gou.RegisterProcessHandler("xiang.helper.For", ProcessFor)
|
||||
|
|
@ -26,23 +22,7 @@ func init() {
|
|||
// ProcessArrayPluck xiang.helper.ArrayPluck 将多个数据记录集合,合并为一个数据记录集合
|
||||
func ProcessArrayPluck(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
columnsAny := process.Args[0]
|
||||
columns := []string{}
|
||||
switch columnsAny.(type) {
|
||||
case []interface{}:
|
||||
for _, v := range columnsAny.([]interface{}) {
|
||||
value, ok := v.(string)
|
||||
if ok {
|
||||
columns = append(columns, value)
|
||||
continue
|
||||
}
|
||||
exception.New("参数错误: 第1个参数不是字符串数组", 400).Ctx(process.Args[0]).Throw()
|
||||
}
|
||||
case []string:
|
||||
default:
|
||||
exception.New("参数错误: 第1个参数不是字符串数组", 400).Ctx(process.Args[0]).Throw()
|
||||
break
|
||||
}
|
||||
columns := process.ArgsStrings(0)
|
||||
pluck := process.ArgsMap(1)
|
||||
return ArrayPluck(columns, pluck)
|
||||
}
|
||||
|
|
@ -50,33 +30,7 @@ func ProcessArrayPluck(process *gou.Process) interface{} {
|
|||
// ProcessArraySplit xiang.helper.ArraySplit 将多条数记录集合,分解为一个 columns:[]string 和 values: [][]interface{}
|
||||
func ProcessArraySplit(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
args := process.Args[0]
|
||||
records := []map[string]interface{}{}
|
||||
|
||||
switch args.(type) {
|
||||
case []interface{}:
|
||||
for _, v := range args.([]interface{}) {
|
||||
value, ok := v.(map[string]interface{})
|
||||
if ok {
|
||||
records = append(records, value)
|
||||
continue
|
||||
}
|
||||
exception.New("参数错误: 第1个参数不是数组", 400).Ctx(fmt.Sprintf("%#v", process.Args[0])).Throw()
|
||||
}
|
||||
break
|
||||
case []share.Record:
|
||||
for _, v := range args.([]share.Record) {
|
||||
records = append(records, v)
|
||||
}
|
||||
break
|
||||
case []map[string]interface{}:
|
||||
records = args.([]map[string]interface{})
|
||||
break
|
||||
default:
|
||||
fmt.Printf("%#v %s\n", args, reflect.TypeOf(args).Kind())
|
||||
exception.New("参数错误: 第1个参数不是数组", 400).Ctx(fmt.Sprintf("%#v", process.Args[0])).Throw()
|
||||
break
|
||||
}
|
||||
records := process.ArgsRecords(0)
|
||||
columns, values := ArraySplit(records)
|
||||
return map[string]interface{}{
|
||||
"columns": columns,
|
||||
|
|
@ -87,34 +41,8 @@ func ProcessArraySplit(process *gou.Process) interface{} {
|
|||
// ProcessArrayColumn xiang.helper.ArrayColumn 返回多条数据记录,指定字段数值。
|
||||
func ProcessArrayColumn(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
args := process.Args[0]
|
||||
records := []map[string]interface{}{}
|
||||
records := process.ArgsRecords(0)
|
||||
name := process.ArgsString(1)
|
||||
|
||||
switch args.(type) {
|
||||
case []interface{}:
|
||||
for _, v := range args.([]interface{}) {
|
||||
value, ok := v.(map[string]interface{})
|
||||
if ok {
|
||||
records = append(records, value)
|
||||
continue
|
||||
}
|
||||
exception.New("参数错误: 第1个参数不是数组", 400).Ctx(fmt.Sprintf("%#v", process.Args[0])).Throw()
|
||||
}
|
||||
break
|
||||
case []share.Record:
|
||||
for _, v := range args.([]share.Record) {
|
||||
records = append(records, v)
|
||||
}
|
||||
break
|
||||
case []map[string]interface{}:
|
||||
records = args.([]map[string]interface{})
|
||||
break
|
||||
default:
|
||||
fmt.Printf("%#v %s\n", args, reflect.TypeOf(args).Kind())
|
||||
exception.New("参数错误: 第1个参数不是数组", 400).Ctx(fmt.Sprintf("%#v", process.Args[0])).Throw()
|
||||
break
|
||||
}
|
||||
values := ArrayColumn(records, name)
|
||||
return values
|
||||
}
|
||||
|
|
@ -122,55 +50,19 @@ func ProcessArrayColumn(process *gou.Process) interface{} {
|
|||
// ProcessArrayKeep xiang.helper.ArrayKeep 仅保留指定键名的数据
|
||||
func ProcessArrayKeep(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
args := process.Args[0]
|
||||
columnsAny := process.Args[1]
|
||||
records := []map[string]interface{}{}
|
||||
columns := []string{}
|
||||
|
||||
switch args.(type) {
|
||||
case []interface{}:
|
||||
for _, v := range args.([]interface{}) {
|
||||
value, ok := v.(map[string]interface{})
|
||||
if ok {
|
||||
records = append(records, value)
|
||||
continue
|
||||
}
|
||||
exception.New("参数错误: 第1个参数不是数组", 400).Ctx(fmt.Sprintf("%#v", process.Args[0])).Throw()
|
||||
}
|
||||
break
|
||||
case []share.Record:
|
||||
for _, v := range args.([]share.Record) {
|
||||
records = append(records, v)
|
||||
}
|
||||
break
|
||||
case []map[string]interface{}:
|
||||
records = args.([]map[string]interface{})
|
||||
break
|
||||
default:
|
||||
fmt.Printf("%#v %s\n", args, reflect.TypeOf(args).Kind())
|
||||
exception.New("参数错误: 第1个参数不是数组", 400).Ctx(fmt.Sprintf("%#v", process.Args[0])).Throw()
|
||||
break
|
||||
}
|
||||
|
||||
switch columnsAny.(type) {
|
||||
case []interface{}:
|
||||
for _, v := range columnsAny.([]interface{}) {
|
||||
value, ok := v.(string)
|
||||
if ok {
|
||||
columns = append(columns, value)
|
||||
continue
|
||||
}
|
||||
exception.New("参数错误: 第2个参数不是字符串数组", 400).Ctx(process.Args[0]).Throw()
|
||||
}
|
||||
case []string:
|
||||
default:
|
||||
exception.New("参数错误: 第2个参数不是字符串数组", 400).Ctx(process.Args[0]).Throw()
|
||||
break
|
||||
}
|
||||
|
||||
records := process.ArgsRecords(0)
|
||||
columns := process.ArgsStrings(1)
|
||||
return ArrayKeep(records, columns)
|
||||
}
|
||||
|
||||
// ProcessArrayTree xiang.helper.ArrayTree 转换为属性结构
|
||||
func ProcessArrayTree(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
records := process.ArgsRecords(0)
|
||||
setting := process.ArgsMap(1)
|
||||
return ArrayTree(records, setting)
|
||||
}
|
||||
|
||||
// ProcessMapValues xiang.helper.MapValues 返回映射的数值
|
||||
func ProcessMapValues(process *gou.Process) interface{} {
|
||||
process.ValidateArgNums(1)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue