Skip to content

Commit 0de7d92

Browse files
committed
Merge pull request #165 from codekitchen/groupByLabel
add groupByLabel function
2 parents 5f59705 + 7e9ddbc commit 0de7d92

File tree

3 files changed

+88
-5
lines changed

3 files changed

+88
-5
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ For example, this is a JSON version of an emitted RuntimeContainer struct:
349349
* *`groupBy $containers $fieldPath`*: Groups an array of `RuntimeContainer` instances based on the values of a field path expression `$fieldPath`. A field path expression is a dot-delimited list of map keys or struct member names specifying the path from container to a nested value, which must be a string. Returns a map from the value of the field path expression to an array of containers having that value. Containers that do not have a value for the field path in question are omitted.
350350
* *`groupByKeys $containers $fieldPath`*: Returns the same as `groupBy` but only returns the keys of the map.
351351
* *`groupByMulti $containers $fieldPath $sep`*: Like `groupBy`, but the string value specified by `$fieldPath` is first split by `$sep` into a list of strings. A container whose `$fieldPath` value contains a list of strings will show up in the map output under each of those strings.
352+
* *`groupByLabel $containers $label`*: Returns the same as `groupBy` but grouping by the given label's value.
352353
* *`hasPrefix $prefix $string`*: Returns whether `$prefix` is a prefix of `$string`.
353354
* *`hasSuffix $suffix $string`*: Returns whether `$suffix` is a suffix of `$string`.
354355
* *`intersect $slice1 $slice2`*: Returns the strings that exist in both string slices.

template.go

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func getArrayValues(funcName string, entries interface{}) (*reflect.Value, error
5151
}
5252

5353
// Generalized groupBy function
54-
func generalizedGroupBy(funcName string, entries interface{}, key string, addEntry func(map[string][]interface{}, interface{}, interface{})) (map[string][]interface{}, error) {
54+
func generalizedGroupBy(funcName string, entries interface{}, getValue func(interface{}) (interface{}, error), addEntry func(map[string][]interface{}, interface{}, interface{})) (map[string][]interface{}, error) {
5555
entriesVal, err := getArrayValues(funcName, entries)
5656

5757
if err != nil {
@@ -61,16 +61,26 @@ func generalizedGroupBy(funcName string, entries interface{}, key string, addEnt
6161
groups := make(map[string][]interface{})
6262
for i := 0; i < entriesVal.Len(); i++ {
6363
v := reflect.Indirect(entriesVal.Index(i)).Interface()
64-
value := deepGet(v, key)
64+
value, err := getValue(v)
65+
if err != nil {
66+
return nil, err
67+
}
6568
if value != nil {
6669
addEntry(groups, value, v)
6770
}
6871
}
6972
return groups, nil
7073
}
7174

75+
func generalizedGroupByKey(funcName string, entries interface{}, key string, addEntry func(map[string][]interface{}, interface{}, interface{})) (map[string][]interface{}, error) {
76+
getKey := func(v interface{}) (interface{}, error) {
77+
return deepGet(v, key), nil
78+
}
79+
return generalizedGroupBy(funcName, entries, getKey, addEntry)
80+
}
81+
7282
func groupByMulti(entries interface{}, key, sep string) (map[string][]interface{}, error) {
73-
return generalizedGroupBy("groupByMulti", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
83+
return generalizedGroupByKey("groupByMulti", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
7484
items := strings.Split(value.(string), sep)
7585
for _, item := range items {
7686
groups[item] = append(groups[item], v)
@@ -80,14 +90,14 @@ func groupByMulti(entries interface{}, key, sep string) (map[string][]interface{
8090

8191
// groupBy groups a generic array or slice by the path property key
8292
func groupBy(entries interface{}, key string) (map[string][]interface{}, error) {
83-
return generalizedGroupBy("groupBy", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
93+
return generalizedGroupByKey("groupBy", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
8494
groups[value.(string)] = append(groups[value.(string)], v)
8595
})
8696
}
8797

8898
// groupByKeys is the same as groupBy but only returns a list of keys
8999
func groupByKeys(entries interface{}, key string) ([]string, error) {
90-
keys, err := generalizedGroupBy("groupByKeys", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
100+
keys, err := generalizedGroupByKey("groupByKeys", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
91101
groups[value.(string)] = append(groups[value.(string)], v)
92102
})
93103

@@ -102,6 +112,22 @@ func groupByKeys(entries interface{}, key string) ([]string, error) {
102112
return ret, nil
103113
}
104114

115+
// groupByLabel is the same as groupBy but over a given label
116+
func groupByLabel(entries interface{}, label string) (map[string][]interface{}, error) {
117+
getLabel := func(v interface{}) (interface{}, error) {
118+
if container, ok := v.(RuntimeContainer); ok {
119+
if value, ok := container.Labels[label]; ok {
120+
return value, nil
121+
}
122+
return nil, nil
123+
}
124+
return nil, fmt.Errorf("Must pass an array or slice of RuntimeContainer to 'groupByLabel'; received %v", v)
125+
}
126+
return generalizedGroupBy("groupByLabel", entries, getLabel, func(groups map[string][]interface{}, value interface{}, v interface{}) {
127+
groups[value.(string)] = append(groups[value.(string)], v)
128+
})
129+
}
130+
105131
// Generalized where function
106132
func generalizedWhere(funcName string, entries interface{}, key string, test func(interface{}) bool) (interface{}, error) {
107133
entriesVal, err := getArrayValues(funcName, entries)
@@ -396,6 +422,7 @@ func newTemplate(name string) *template.Template {
396422
"groupBy": groupBy,
397423
"groupByKeys": groupByKeys,
398424
"groupByMulti": groupByMulti,
425+
"groupByLabel": groupByLabel,
399426
"hasPrefix": hasPrefix,
400427
"hasSuffix": hasSuffix,
401428
"json": marshalJson,

template_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,61 @@ func TestGroupByAfterWhere(t *testing.T) {
186186
}
187187
}
188188

189+
func TestGroupByLabel(t *testing.T) {
190+
containers := []*RuntimeContainer{
191+
&RuntimeContainer{
192+
Labels: map[string]string{
193+
"com.docker.compose.project": "one",
194+
},
195+
ID: "1",
196+
},
197+
&RuntimeContainer{
198+
Labels: map[string]string{
199+
"com.docker.compose.project": "two",
200+
},
201+
ID: "2",
202+
},
203+
&RuntimeContainer{
204+
Labels: map[string]string{
205+
"com.docker.compose.project": "one",
206+
},
207+
ID: "3",
208+
},
209+
&RuntimeContainer{
210+
ID: "4",
211+
},
212+
&RuntimeContainer{
213+
Labels: map[string]string{
214+
"com.docker.compose.project": "",
215+
},
216+
ID: "5",
217+
},
218+
}
219+
220+
groups, err := groupByLabel(containers, "com.docker.compose.project")
221+
if err != nil {
222+
t.FailNow()
223+
}
224+
225+
if len(groups) != 3 {
226+
t.Fail()
227+
}
228+
229+
if len(groups["one"]) != 2 {
230+
t.Fail()
231+
}
232+
if len(groups[""]) != 1 {
233+
t.Fail()
234+
}
235+
236+
if len(groups["two"]) != 1 {
237+
t.FailNow()
238+
}
239+
if groups["two"][0].(RuntimeContainer).ID != "2" {
240+
t.Fail()
241+
}
242+
}
243+
189244
func TestGroupByMulti(t *testing.T) {
190245
containers := []*RuntimeContainer{
191246
&RuntimeContainer{

0 commit comments

Comments
 (0)