diff --git a/src/fmt/print.go b/src/fmt/print.go index 8d6d961228..f9f200499d 100644 --- a/src/fmt/print.go +++ b/src/fmt/print.go @@ -814,7 +814,7 @@ func (p *pp) printValue(value reflect.Value, verb rune, depth int) { p.buf.writeString(mapString) } sorted := fmtsort.Sort(f) - for i, key := range sorted.Key { + for i, m := range sorted { if i > 0 { if p.fmt.sharpV { p.buf.writeString(commaSpaceString) @@ -822,9 +822,9 @@ func (p *pp) printValue(value reflect.Value, verb rune, depth int) { p.buf.writeByte(' ') } } - p.printValue(key, verb, depth+1) + p.printValue(m.Key, verb, depth+1) p.buf.writeByte(':') - p.printValue(sorted.Value[i], verb, depth+1) + p.printValue(m.Value, verb, depth+1) } if p.fmt.sharpV { p.buf.writeByte('}') diff --git a/src/internal/fmtsort/sort.go b/src/internal/fmtsort/sort.go index 278a89bd75..ea042e1811 100644 --- a/src/internal/fmtsort/sort.go +++ b/src/internal/fmtsort/sort.go @@ -10,24 +10,21 @@ package fmtsort import ( "reflect" - "sort" + "slices" ) // Note: Throughout this package we avoid calling reflect.Value.Interface as // it is not always legal to do so and it's easier to avoid the issue than to face it. -// SortedMap represents a map's keys and values. The keys and values are -// aligned in index order: Value[i] is the value in the map corresponding to Key[i]. -type SortedMap struct { - Key []reflect.Value - Value []reflect.Value -} +// SortedMap is a slice of KeyValue pairs that simplifies sorting +// and iterating over map entries. +// +// Each KeyValue pair contains a map key and its corresponding value. +type SortedMap []KeyValue -func (o *SortedMap) Len() int { return len(o.Key) } -func (o *SortedMap) Less(i, j int) bool { return compare(o.Key[i], o.Key[j]) < 0 } -func (o *SortedMap) Swap(i, j int) { - o.Key[i], o.Key[j] = o.Key[j], o.Key[i] - o.Value[i], o.Value[j] = o.Value[j], o.Value[i] +// KeyValue holds a single key and value pair found in a map. +type KeyValue struct { + Key, Value reflect.Value } // Sort accepts a map and returns a SortedMap that has the same keys and @@ -48,7 +45,7 @@ func (o *SortedMap) Swap(i, j int) { // Otherwise identical arrays compare by length. // - interface values compare first by reflect.Type describing the concrete type // and then by concrete value as described in the previous rules. -func Sort(mapValue reflect.Value) *SortedMap { +func Sort(mapValue reflect.Value) SortedMap { if mapValue.Type().Kind() != reflect.Map { return nil } @@ -56,18 +53,14 @@ func Sort(mapValue reflect.Value) *SortedMap { // of a concurrent map update. The runtime is responsible for // yelling loudly if that happens. See issue 33275. n := mapValue.Len() - key := make([]reflect.Value, 0, n) - value := make([]reflect.Value, 0, n) + sorted := make(SortedMap, 0, n) iter := mapValue.MapRange() for iter.Next() { - key = append(key, iter.Key()) - value = append(value, iter.Value()) + sorted = append(sorted, KeyValue{iter.Key(), iter.Value()}) } - sorted := &SortedMap{ - Key: key, - Value: value, - } - sort.Stable(sorted) + slices.SortStableFunc(sorted, func(a, b KeyValue) int { + return compare(a.Key, b.Key) + }) return sorted } diff --git a/src/internal/fmtsort/sort_test.go b/src/internal/fmtsort/sort_test.go index 7d5de9f56b..29a9c2c43f 100644 --- a/src/internal/fmtsort/sort_test.go +++ b/src/internal/fmtsort/sort_test.go @@ -142,13 +142,13 @@ func sprint(data any) string { return "nil" } b := new(strings.Builder) - for i, key := range om.Key { + for i, m := range om { if i > 0 { b.WriteRune(' ') } - b.WriteString(sprintKey(key)) + b.WriteString(sprintKey(m.Key)) b.WriteRune(':') - fmt.Fprint(b, om.Value[i]) + fmt.Fprint(b, m.Value) } return b.String() } diff --git a/src/text/template/exec.go b/src/text/template/exec.go index 4c899b1c79..1a8f2fa0df 100644 --- a/src/text/template/exec.go +++ b/src/text/template/exec.go @@ -408,8 +408,8 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { break } om := fmtsort.Sort(val) - for i, key := range om.Key { - oneIteration(key, om.Value[i]) + for _, m := range om { + oneIteration(m.Key, m.Value) } return case reflect.Chan: