/
gutil_list.go
132 lines (125 loc) · 3.6 KB
/
gutil_list.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gutil
import (
"reflect"
)
// ListItemValues retrieves and returns the elements of all item struct/map with key `key`.
// Note that the parameter `list` should be type of slice which contains elements of map or struct,
// or else it returns an empty slice.
//
// The parameter `list` supports types like:
// []map[string]interface{}
// []map[string]sub-map
// []struct
// []struct:sub-struct
// Note that the sub-map/sub-struct makes sense only if the optional parameter `subKey` is given.
func ListItemValues(list interface{}, key interface{}, subKey ...interface{}) (values []interface{}) {
var reflectValue reflect.Value
if v, ok := list.(reflect.Value); ok {
reflectValue = v
} else {
reflectValue = reflect.ValueOf(list)
}
reflectKind := reflectValue.Kind()
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch reflectKind {
case reflect.Slice, reflect.Array:
if reflectValue.Len() == 0 {
return
}
values = []interface{}{}
for i := 0; i < reflectValue.Len(); i++ {
if value, ok := ItemValue(reflectValue.Index(i), key); ok {
if len(subKey) > 0 && subKey[0] != nil {
if subValue, ok := ItemValue(value, subKey[0]); ok {
value = subValue
} else {
continue
}
}
if array, ok := value.([]interface{}); ok {
values = append(values, array...)
} else {
values = append(values, value)
}
}
}
}
return
}
// ItemValue retrieves and returns its value of which name/attribute specified by `key`.
// The parameter `item` can be type of map/*map/struct/*struct.
func ItemValue(item interface{}, key interface{}) (value interface{}, found bool) {
var reflectValue reflect.Value
if v, ok := item.(reflect.Value); ok {
reflectValue = v
} else {
reflectValue = reflect.ValueOf(item)
}
reflectKind := reflectValue.Kind()
if reflectKind == reflect.Interface {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
var keyValue reflect.Value
if v, ok := key.(reflect.Value); ok {
keyValue = v
} else {
keyValue = reflect.ValueOf(key)
}
switch reflectKind {
case reflect.Array, reflect.Slice:
// The `key` must be type of string.
values := ListItemValues(reflectValue, keyValue.String())
if values == nil {
return nil, false
}
return values, true
case reflect.Map:
v := reflectValue.MapIndex(keyValue)
if v.IsValid() {
found = true
value = v.Interface()
}
case reflect.Struct:
// The `mapKey` must be type of string.
v := reflectValue.FieldByName(keyValue.String())
if v.IsValid() {
found = true
value = v.Interface()
}
}
return
}
// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key `key`.
// Note that the parameter `list` should be type of slice which contains elements of map or struct,
// or else it returns an empty slice.
func ListItemValuesUnique(list interface{}, key string, subKey ...interface{}) []interface{} {
values := ListItemValues(list, key, subKey...)
if len(values) > 0 {
var (
ok bool
m = make(map[interface{}]struct{}, len(values))
)
for i := 0; i < len(values); {
if _, ok = m[values[i]]; ok {
values = SliceDelete(values, i)
} else {
m[values[i]] = struct{}{}
i++
}
}
}
return values
}