From 56534beaf684f374327b6d01be26d15ae41c8985 Mon Sep 17 00:00:00 2001 From: Tatsushi Demachi Date: Fri, 7 Aug 2015 02:27:15 +0900 Subject: [PATCH] Fix sort tpl func to return explicit type value sort template function returns `[]interface{}` type slice value regardless of its original element type. This fixes it to keep the original element type. For example, if it sorts `map[string]int` type value, it returns `[]int` slice value instead of `[]interface{}` slice value. --- tpl/template_funcs.go | 17 ++++++++------- tpl/template_funcs_test.go | 42 +++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/tpl/template_funcs.go b/tpl/template_funcs.go index 041f954b6..a7cc38af7 100644 --- a/tpl/template_funcs.go +++ b/tpl/template_funcs.go @@ -875,7 +875,7 @@ func Delimit(seq, delimiter interface{}, last ...interface{}) (template.HTML, er return template.HTML(str), nil } -func Sort(seq interface{}, args ...interface{}) ([]interface{}, error) { +func Sort(seq interface{}, args ...interface{}) (interface{}, error) { seqv := reflect.ValueOf(seq) seqv, isNil := indirect(seqv) if isNil { @@ -883,7 +883,7 @@ func Sort(seq interface{}, args ...interface{}) ([]interface{}, error) { } // Create a list of pairs that will be used to do the sort - p := pairList{SortAsc: true} + p := pairList{SortAsc: true, SliceType: reflect.SliceOf(seqv.Type().Elem())} p.Pairs = make([]pair, seqv.Len()) for i, l := range args { @@ -900,7 +900,6 @@ func Sort(seq interface{}, args ...interface{}) ([]interface{}, error) { } } - var sorted []interface{} switch seqv.Kind() { case reflect.Array, reflect.Slice: for i := 0; i < seqv.Len(); i++ { @@ -921,8 +920,7 @@ func Sort(seq interface{}, args ...interface{}) ([]interface{}, error) { default: return nil, errors.New("can't sort " + reflect.ValueOf(seq).Type().String()) } - sorted = p.sort() - return sorted, nil + return p.sort(), nil } // Credit for pair sorting method goes to Andrew Gerrand @@ -938,6 +936,7 @@ type pairList struct { Pairs []pair SortByField string SortAsc bool + SliceType reflect.Type } func (p pairList) Swap(i, j int) { p.Pairs[i], p.Pairs[j] = p.Pairs[j], p.Pairs[i] } @@ -965,18 +964,18 @@ func (p pairList) Less(i, j int) bool { } // sorts a pairList and returns a slice of sorted values -func (p pairList) sort() []interface{} { +func (p pairList) sort() interface{} { if p.SortAsc { sort.Sort(p) } else { sort.Sort(sort.Reverse(p)) } - sorted := make([]interface{}, len(p.Pairs)) + sorted := reflect.MakeSlice(p.SliceType, len(p.Pairs), len(p.Pairs)) for i, v := range p.Pairs { - sorted[i] = v.Value.Interface() + sorted.Index(i).Set(v.Value) } - return sorted + return sorted.Interface() } func IsSet(a interface{}, key interface{}) bool { diff --git a/tpl/template_funcs_test.go b/tpl/template_funcs_test.go index 633d6e4d5..c2e9baff9 100644 --- a/tpl/template_funcs_test.go +++ b/tpl/template_funcs_test.go @@ -1085,48 +1085,48 @@ func TestSort(t *testing.T) { sequence interface{} sortByField interface{} sortAsc string - expect []interface{} + expect interface{} }{ - {[]string{"class1", "class2", "class3"}, nil, "asc", []interface{}{"class1", "class2", "class3"}}, - {[]string{"class3", "class1", "class2"}, nil, "asc", []interface{}{"class1", "class2", "class3"}}, - {[]int{1, 2, 3, 4, 5}, nil, "asc", []interface{}{1, 2, 3, 4, 5}}, - {[]int{5, 4, 3, 1, 2}, nil, "asc", []interface{}{1, 2, 3, 4, 5}}, + {[]string{"class1", "class2", "class3"}, nil, "asc", []string{"class1", "class2", "class3"}}, + {[]string{"class3", "class1", "class2"}, nil, "asc", []string{"class1", "class2", "class3"}}, + {[]int{1, 2, 3, 4, 5}, nil, "asc", []int{1, 2, 3, 4, 5}}, + {[]int{5, 4, 3, 1, 2}, nil, "asc", []int{1, 2, 3, 4, 5}}, // test map sorting by keys - {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, nil, "asc", []interface{}{10, 20, 30, 40, 50}}, - {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, nil, "asc", []interface{}{30, 20, 10, 40, 50}}, - {map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, nil, "asc", []interface{}{"10", "20", "30", "40", "50"}}, - {map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, nil, "asc", []interface{}{"30", "20", "10", "40", "50"}}, - {map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, nil, "asc", []interface{}{"50", "40", "10", "30", "20"}}, - {map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, nil, "asc", []interface{}{"10", "20", "30", "40", "50"}}, - {map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, nil, "asc", []interface{}{"30", "20", "10", "40", "50"}}, - {map[float64]string{3.3: "10", 2.3: "20", 1.3: "30", 4.3: "40", 5.3: "50"}, nil, "asc", []interface{}{"30", "20", "10", "40", "50"}}, + {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, nil, "asc", []int{10, 20, 30, 40, 50}}, + {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, nil, "asc", []int{30, 20, 10, 40, 50}}, + {map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, nil, "asc", []string{"10", "20", "30", "40", "50"}}, + {map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, nil, "asc", []string{"30", "20", "10", "40", "50"}}, + {map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, nil, "asc", []string{"50", "40", "10", "30", "20"}}, + {map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, nil, "asc", []string{"10", "20", "30", "40", "50"}}, + {map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, nil, "asc", []string{"30", "20", "10", "40", "50"}}, + {map[float64]string{3.3: "10", 2.3: "20", 1.3: "30", 4.3: "40", 5.3: "50"}, nil, "asc", []string{"30", "20", "10", "40", "50"}}, // test map sorting by value - {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "value", "asc", []interface{}{10, 20, 30, 40, 50}}, - {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "value", "asc", []interface{}{10, 20, 30, 40, 50}}, + {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "value", "asc", []int{10, 20, 30, 40, 50}}, + {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "value", "asc", []int{10, 20, 30, 40, 50}}, // test map sorting by field value { map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}}, "MyInt", "asc", - []interface{}{ts{10, 10.5, "ten"}, ts{20, 20.5, "twenty"}, ts{30, 30.5, "thirty"}, ts{40, 40.5, "forty"}, ts{50, 50.5, "fifty"}}, + []ts{{10, 10.5, "ten"}, {20, 20.5, "twenty"}, {30, 30.5, "thirty"}, {40, 40.5, "forty"}, {50, 50.5, "fifty"}}, }, { map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}}, "MyFloat", "asc", - []interface{}{ts{10, 10.5, "ten"}, ts{20, 20.5, "twenty"}, ts{30, 30.5, "thirty"}, ts{40, 40.5, "forty"}, ts{50, 50.5, "fifty"}}, + []ts{{10, 10.5, "ten"}, {20, 20.5, "twenty"}, {30, 30.5, "thirty"}, {40, 40.5, "forty"}, {50, 50.5, "fifty"}}, }, { map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}}, "MyString", "asc", - []interface{}{ts{50, 50.5, "fifty"}, ts{40, 40.5, "forty"}, ts{10, 10.5, "ten"}, ts{30, 30.5, "thirty"}, ts{20, 20.5, "twenty"}}, + []ts{{50, 50.5, "fifty"}, {40, 40.5, "forty"}, {10, 10.5, "ten"}, {30, 30.5, "thirty"}, {20, 20.5, "twenty"}}, }, // Test sort desc - {[]string{"class1", "class2", "class3"}, "value", "desc", []interface{}{"class3", "class2", "class1"}}, - {[]string{"class3", "class1", "class2"}, "value", "desc", []interface{}{"class3", "class2", "class1"}}, + {[]string{"class1", "class2", "class3"}, "value", "desc", []string{"class3", "class2", "class1"}}, + {[]string{"class3", "class1", "class2"}, "value", "desc", []string{"class3", "class2", "class1"}}, } { - var result []interface{} + var result interface{} var err error if this.sortByField == nil { result, err = Sort(this.sequence)