mirror of
https://github.com/golang/go
synced 2024-11-25 01:08:02 -07:00
template: for range on a map, sort the keys if feasible.
Fixes #2696. R=golang-dev, rsc CC=golang-dev https://golang.org/cl/5543055
This commit is contained in:
parent
92c8df46c6
commit
a5950df89e
@ -50,7 +50,9 @@ data, defined in detail below.
|
|||||||
The value of the pipeline must be an array, slice, or map. If
|
The value of the pipeline must be an array, slice, or map. If
|
||||||
the value of the pipeline has length zero, nothing is output;
|
the value of the pipeline has length zero, nothing is output;
|
||||||
otherwise, dot is set to the successive elements of the array,
|
otherwise, dot is set to the successive elements of the array,
|
||||||
slice, or map and T1 is executed.
|
slice, or map and T1 is executed. If the value is a map and the
|
||||||
|
keys are of basic type with a defined order ("comparable"), the
|
||||||
|
elements will be visited in sorted key order.
|
||||||
|
|
||||||
{{range pipeline}} T1 {{else}} T0 {{end}}
|
{{range pipeline}} T1 {{else}} T0 {{end}}
|
||||||
The value of the pipeline must be an array, slice, or map. If
|
The value of the pipeline must be an array, slice, or map. If
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template/parse"
|
"text/template/parse"
|
||||||
)
|
)
|
||||||
@ -234,7 +235,7 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
|
|||||||
if val.Len() == 0 {
|
if val.Len() == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
for _, key := range val.MapKeys() {
|
for _, key := range sortKeys(val.MapKeys()) {
|
||||||
oneIteration(key, val.MapIndex(key))
|
oneIteration(key, val.MapIndex(key))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -676,3 +677,44 @@ func (s *state) printValue(n parse.Node, v reflect.Value) {
|
|||||||
}
|
}
|
||||||
fmt.Fprint(s.wr, v.Interface())
|
fmt.Fprint(s.wr, v.Interface())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Types to help sort the keys in a map for reproducible output.
|
||||||
|
|
||||||
|
type rvs []reflect.Value
|
||||||
|
|
||||||
|
func (x rvs) Len() int { return len(x) }
|
||||||
|
func (x rvs) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||||
|
|
||||||
|
type rvInts struct{ rvs }
|
||||||
|
|
||||||
|
func (x rvInts) Less(i, j int) bool { return x.rvs[i].Int() < x.rvs[j].Int() }
|
||||||
|
|
||||||
|
type rvUints struct{ rvs }
|
||||||
|
|
||||||
|
func (x rvUints) Less(i, j int) bool { return x.rvs[i].Uint() < x.rvs[j].Uint() }
|
||||||
|
|
||||||
|
type rvFloats struct{ rvs }
|
||||||
|
|
||||||
|
func (x rvFloats) Less(i, j int) bool { return x.rvs[i].Float() < x.rvs[j].Float() }
|
||||||
|
|
||||||
|
type rvStrings struct{ rvs }
|
||||||
|
|
||||||
|
func (x rvStrings) Less(i, j int) bool { return x.rvs[i].String() < x.rvs[j].String() }
|
||||||
|
|
||||||
|
// sortKeys sorts (if it can) the slice of reflect.Values, which is a slice of map keys.
|
||||||
|
func sortKeys(v []reflect.Value) []reflect.Value {
|
||||||
|
if len(v) <= 1 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
switch v[0].Kind() {
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
sort.Sort(rvFloats{v})
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
sort.Sort(rvInts{v})
|
||||||
|
case reflect.String:
|
||||||
|
sort.Sort(rvStrings{v})
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
sort.Sort(rvUints{v})
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -169,18 +168,6 @@ func (t *T) MAdd(a int, b []int) []int {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// MSort is used to sort map keys for stable output. (Nice trick!)
|
|
||||||
func (t *T) MSort(m map[string]int) []string {
|
|
||||||
keys := make([]string, len(m))
|
|
||||||
i := 0
|
|
||||||
for k := range m {
|
|
||||||
keys[i] = k
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// EPERM returns a value and an error according to its argument.
|
// EPERM returns a value and an error according to its argument.
|
||||||
func (t *T) EPERM(error bool) (bool, error) {
|
func (t *T) EPERM(error bool) (bool, error) {
|
||||||
if error {
|
if error {
|
||||||
@ -410,9 +397,9 @@ var execTests = []execTest{
|
|||||||
{"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
{"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
||||||
{"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
|
{"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
|
||||||
{"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
|
{"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
|
||||||
{"range map", "{{range .MSI | .MSort}}-{{.}}-{{end}}", "-one--three--two-", tVal, true},
|
{"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true},
|
||||||
{"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true},
|
{"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true},
|
||||||
{"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", tVal, true},
|
{"range map else", "{{range .MSI}}-{{.}}-{{else}}EMPTY{{end}}", "-1--3--2-", tVal, true},
|
||||||
{"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
{"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
||||||
{"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true},
|
{"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true},
|
||||||
{"range empty nil", "{{range .Empty0}}-{{.}}-{{end}}", "", tVal, true},
|
{"range empty nil", "{{range .Empty0}}-{{.}}-{{end}}", "", tVal, true},
|
||||||
|
Loading…
Reference in New Issue
Block a user