Skip to content

Commit 3a8ad43

Browse files
committed
generalizedGroupBy with reflection
1 parent 110ecfc commit 3a8ad43

File tree

2 files changed

+110
-35
lines changed

2 files changed

+110
-35
lines changed

template.go

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,52 +29,84 @@ func exists(path string) (bool, error) {
2929
return false, err
3030
}
3131

32-
func groupByMulti(entries []*RuntimeContainer, key, sep string) map[string][]*RuntimeContainer {
33-
groups := make(map[string][]*RuntimeContainer)
34-
for _, v := range entries {
35-
value := deepGet(*v, key)
36-
if value != nil {
37-
items := strings.Split(value.(string), sep)
38-
for _, item := range items {
39-
groups[item] = append(groups[item], v)
40-
}
32+
func getArrayValues(funcName string, entries interface{}) (reflect.Value, error) {
33+
entriesVal := reflect.ValueOf(entries)
4134

42-
}
35+
kind := entriesVal.Kind()
36+
37+
if kind == reflect.Ptr {
38+
entriesVal = reflect.Indirect(entriesVal)
39+
kind = entriesVal.Kind()
4340
}
44-
return groups
41+
42+
switch entriesVal.Kind() {
43+
case reflect.Array, reflect.Slice:
44+
break
45+
default:
46+
return entriesVal, fmt.Errorf("Must pass an array or slice to '%v'; received %v; kind %v", funcName, entries, kind)
47+
}
48+
return entriesVal, nil
4549
}
4650

47-
// groupBy groups a list of *RuntimeContainers by the path property key
48-
func groupBy(entries []*RuntimeContainer, key string) map[string][]*RuntimeContainer {
49-
groups := make(map[string][]*RuntimeContainer)
50-
for _, v := range entries {
51-
value := deepGet(*v, key)
51+
// Generalized groupBy function
52+
func generalizedGroupBy(funcName string, entries interface{}, key string, addEntry func(map[string][]interface{}, interface{}, interface{})) (map[string][]interface{}, error) {
53+
entriesVal, err := getArrayValues(funcName, entries)
54+
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
groups := make(map[string][]interface{})
60+
for i := 0; i < entriesVal.Len(); i++ {
61+
v := reflect.Indirect(entriesVal.Index(i)).Interface()
62+
value := deepGet(v, key)
5263
if value != nil {
53-
groups[value.(string)] = append(groups[value.(string)], v)
64+
addEntry(groups, value, v)
5465
}
5566
}
56-
return groups
67+
return groups, nil
68+
}
69+
70+
func groupByMulti(entries interface{}, key, sep string) (map[string][]interface{}, error) {
71+
return generalizedGroupBy("groupByMulti", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
72+
items := strings.Split(value.(string), sep)
73+
for _, item := range items {
74+
groups[item] = append(groups[item], v)
75+
}
76+
})
77+
}
78+
79+
// groupBy groups a generic array or slice by the path property key
80+
func groupBy(entries interface{}, key string) (map[string][]interface{}, error) {
81+
return generalizedGroupBy("groupBy", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
82+
groups[value.(string)] = append(groups[value.(string)], v)
83+
})
5784
}
5885

5986
// groupByKeys is the same as groupBy but only returns a list of keys
60-
func groupByKeys(entries []*RuntimeContainer, key string) []string {
61-
groups := groupBy(entries, key)
87+
func groupByKeys(entries interface{}, key string) ([]string, error) {
88+
keys, err := generalizedGroupBy("groupByKeys", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
89+
groups[value.(string)] = append(groups[value.(string)], v)
90+
})
91+
92+
if err != nil {
93+
return nil, err
94+
}
95+
6296
ret := []string{}
63-
for k, _ := range groups {
97+
for k := range keys {
6498
ret = append(ret, k)
6599
}
66-
return ret
100+
return ret, nil
67101
}
68102

69103
// Generalized where function
70104
func generalizedWhere(funcName string, entries interface{}, key string, test func(interface{}) bool) (interface{}, error) {
71-
entriesVal := reflect.ValueOf(entries)
72105

73-
switch entriesVal.Kind() {
74-
case reflect.Array, reflect.Slice:
75-
break
76-
default:
77-
return nil, fmt.Errorf("Must pass an array or slice to '%s'; received %v", funcName, entries)
106+
entriesVal, err := getArrayValues(funcName, entries)
107+
108+
if err != nil {
109+
return nil, err
78110
}
79111

80112
selection := make([]interface{}, 0)

template_test.go

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func TestGroupByExistingKey(t *testing.T) {
126126
},
127127
}
128128

129-
groups := groupBy(containers, "Env.VIRTUAL_HOST")
129+
groups, _ := groupBy(containers, "Env.VIRTUAL_HOST")
130130
if len(groups) != 2 {
131131
t.Fail()
132132
}
@@ -136,9 +136,52 @@ func TestGroupByExistingKey(t *testing.T) {
136136
}
137137

138138
if len(groups["demo2.localhost"]) != 1 {
139+
t.FailNow()
140+
}
141+
if groups["demo2.localhost"][0].(RuntimeContainer).ID != "3" {
142+
t.Fail()
143+
}
144+
}
145+
146+
func TestGroupByAfterWhere(t *testing.T) {
147+
containers := []*RuntimeContainer{
148+
&RuntimeContainer{
149+
Env: map[string]string{
150+
"VIRTUAL_HOST": "demo1.localhost",
151+
"EXTERNAL": "true",
152+
},
153+
ID: "1",
154+
},
155+
&RuntimeContainer{
156+
Env: map[string]string{
157+
"VIRTUAL_HOST": "demo1.localhost",
158+
},
159+
ID: "2",
160+
},
161+
&RuntimeContainer{
162+
Env: map[string]string{
163+
"VIRTUAL_HOST": "demo2.localhost",
164+
"EXTERNAL": "true",
165+
},
166+
ID: "3",
167+
},
168+
}
169+
170+
filtered, _ := where(containers, "Env.EXTERNAL", "true")
171+
groups, _ := groupBy(filtered, "Env.VIRTUAL_HOST")
172+
173+
if len(groups) != 2 {
174+
t.Fail()
175+
}
176+
177+
if len(groups["demo1.localhost"]) != 1 {
139178
t.Fail()
140179
}
141-
if groups["demo2.localhost"][0].ID != "3" {
180+
181+
if len(groups["demo2.localhost"]) != 1 {
182+
t.FailNow()
183+
}
184+
if groups["demo2.localhost"][0].(RuntimeContainer).ID != "3" {
142185
t.Fail()
143186
}
144187
}
@@ -165,7 +208,7 @@ func TestGroupByMulti(t *testing.T) {
165208
},
166209
}
167210

168-
groups := groupByMulti(containers, "Env.VIRTUAL_HOST", ",")
211+
groups, _ := groupByMulti(containers, "Env.VIRTUAL_HOST", ",")
169212
if len(groups) != 3 {
170213
t.Fatalf("expected 3 got %d", len(groups))
171214
}
@@ -177,14 +220,14 @@ func TestGroupByMulti(t *testing.T) {
177220
if len(groups["demo2.localhost"]) != 1 {
178221
t.Fatalf("expected 1 got %s", len(groups["demo2.localhost"]))
179222
}
180-
if groups["demo2.localhost"][0].ID != "3" {
181-
t.Fatalf("expected 2 got %s", groups["demo2.localhost"][0].ID)
223+
if groups["demo2.localhost"][0].(RuntimeContainer).ID != "3" {
224+
t.Fatalf("expected 2 got %s", groups["demo2.localhost"][0].(RuntimeContainer).ID)
182225
}
183226
if len(groups["demo3.localhost"]) != 1 {
184227
t.Fatalf("expect 1 got %d", len(groups["demo3.localhost"]))
185228
}
186-
if groups["demo3.localhost"][0].ID != "2" {
187-
t.Fatalf("expected 2 got %s", groups["demo3.localhost"][0].ID)
229+
if groups["demo3.localhost"][0].(RuntimeContainer).ID != "2" {
230+
t.Fatalf("expected 2 got %s", groups["demo3.localhost"][0].(RuntimeContainer).ID)
188231
}
189232
}
190233

0 commit comments

Comments
 (0)