Skip to content

Commit

Permalink
generalizedGroupBy with reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
webner committed Jul 10, 2015
1 parent 110ecfc commit 3a8ad43
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 35 deletions.
88 changes: 60 additions & 28 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,52 +29,84 @@ func exists(path string) (bool, error) {
return false, err
}

func groupByMulti(entries []*RuntimeContainer, key, sep string) map[string][]*RuntimeContainer {
groups := make(map[string][]*RuntimeContainer)
for _, v := range entries {
value := deepGet(*v, key)
if value != nil {
items := strings.Split(value.(string), sep)
for _, item := range items {
groups[item] = append(groups[item], v)
}
func getArrayValues(funcName string, entries interface{}) (reflect.Value, error) {
entriesVal := reflect.ValueOf(entries)

}
kind := entriesVal.Kind()

if kind == reflect.Ptr {
entriesVal = reflect.Indirect(entriesVal)
kind = entriesVal.Kind()
}
return groups

switch entriesVal.Kind() {
case reflect.Array, reflect.Slice:
break
default:
return entriesVal, fmt.Errorf("Must pass an array or slice to '%v'; received %v; kind %v", funcName, entries, kind)
}
return entriesVal, nil
}

// groupBy groups a list of *RuntimeContainers by the path property key
func groupBy(entries []*RuntimeContainer, key string) map[string][]*RuntimeContainer {
groups := make(map[string][]*RuntimeContainer)
for _, v := range entries {
value := deepGet(*v, key)
// Generalized groupBy function
func generalizedGroupBy(funcName string, entries interface{}, key string, addEntry func(map[string][]interface{}, interface{}, interface{})) (map[string][]interface{}, error) {
entriesVal, err := getArrayValues(funcName, entries)

if err != nil {
return nil, err
}

groups := make(map[string][]interface{})
for i := 0; i < entriesVal.Len(); i++ {
v := reflect.Indirect(entriesVal.Index(i)).Interface()
value := deepGet(v, key)
if value != nil {
groups[value.(string)] = append(groups[value.(string)], v)
addEntry(groups, value, v)
}
}
return groups
return groups, nil
}

func groupByMulti(entries interface{}, key, sep string) (map[string][]interface{}, error) {
return generalizedGroupBy("groupByMulti", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
items := strings.Split(value.(string), sep)
for _, item := range items {
groups[item] = append(groups[item], v)
}
})
}

// groupBy groups a generic array or slice by the path property key
func groupBy(entries interface{}, key string) (map[string][]interface{}, error) {
return generalizedGroupBy("groupBy", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
groups[value.(string)] = append(groups[value.(string)], v)
})
}

// groupByKeys is the same as groupBy but only returns a list of keys
func groupByKeys(entries []*RuntimeContainer, key string) []string {
groups := groupBy(entries, key)
func groupByKeys(entries interface{}, key string) ([]string, error) {
keys, err := generalizedGroupBy("groupByKeys", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
groups[value.(string)] = append(groups[value.(string)], v)
})

if err != nil {
return nil, err
}

ret := []string{}
for k, _ := range groups {
for k := range keys {
ret = append(ret, k)
}
return ret
return ret, nil
}

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

switch entriesVal.Kind() {
case reflect.Array, reflect.Slice:
break
default:
return nil, fmt.Errorf("Must pass an array or slice to '%s'; received %v", funcName, entries)
entriesVal, err := getArrayValues(funcName, entries)

if err != nil {
return nil, err
}

selection := make([]interface{}, 0)
Expand Down
57 changes: 50 additions & 7 deletions template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func TestGroupByExistingKey(t *testing.T) {
},
}

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

if len(groups["demo2.localhost"]) != 1 {
t.FailNow()
}
if groups["demo2.localhost"][0].(RuntimeContainer).ID != "3" {
t.Fail()
}
}

func TestGroupByAfterWhere(t *testing.T) {
containers := []*RuntimeContainer{
&RuntimeContainer{
Env: map[string]string{
"VIRTUAL_HOST": "demo1.localhost",
"EXTERNAL": "true",
},
ID: "1",
},
&RuntimeContainer{
Env: map[string]string{
"VIRTUAL_HOST": "demo1.localhost",
},
ID: "2",
},
&RuntimeContainer{
Env: map[string]string{
"VIRTUAL_HOST": "demo2.localhost",
"EXTERNAL": "true",
},
ID: "3",
},
}

filtered, _ := where(containers, "Env.EXTERNAL", "true")
groups, _ := groupBy(filtered, "Env.VIRTUAL_HOST")

if len(groups) != 2 {
t.Fail()
}

if len(groups["demo1.localhost"]) != 1 {
t.Fail()
}
if groups["demo2.localhost"][0].ID != "3" {

if len(groups["demo2.localhost"]) != 1 {
t.FailNow()
}
if groups["demo2.localhost"][0].(RuntimeContainer).ID != "3" {
t.Fail()
}
}
Expand All @@ -165,7 +208,7 @@ func TestGroupByMulti(t *testing.T) {
},
}

groups := groupByMulti(containers, "Env.VIRTUAL_HOST", ",")
groups, _ := groupByMulti(containers, "Env.VIRTUAL_HOST", ",")
if len(groups) != 3 {
t.Fatalf("expected 3 got %d", len(groups))
}
Expand All @@ -177,14 +220,14 @@ func TestGroupByMulti(t *testing.T) {
if len(groups["demo2.localhost"]) != 1 {
t.Fatalf("expected 1 got %s", len(groups["demo2.localhost"]))
}
if groups["demo2.localhost"][0].ID != "3" {
t.Fatalf("expected 2 got %s", groups["demo2.localhost"][0].ID)
if groups["demo2.localhost"][0].(RuntimeContainer).ID != "3" {
t.Fatalf("expected 2 got %s", groups["demo2.localhost"][0].(RuntimeContainer).ID)
}
if len(groups["demo3.localhost"]) != 1 {
t.Fatalf("expect 1 got %d", len(groups["demo3.localhost"]))
}
if groups["demo3.localhost"][0].ID != "2" {
t.Fatalf("expected 2 got %s", groups["demo3.localhost"][0].ID)
if groups["demo3.localhost"][0].(RuntimeContainer).ID != "2" {
t.Fatalf("expected 2 got %s", groups["demo3.localhost"][0].(RuntimeContainer).ID)
}
}

Expand Down

0 comments on commit 3a8ad43

Please sign in to comment.