Text template sort map?

I understand map key/value is not stored in sequence order so I expect when I loop map key using range in text/template, the output value should be random. However, the following code always gives me the output of

1
2
3

I expect it can be 1/2/3 or 3/2/1 or 1/3/2 as random pattern?

https://play.golang.org/p/Nu_UsWcqAt

package main

import (
	"os"
	"text/tabwriter"
	"text/template"
)

const (
	ShowFormat = "" +
		"{{range .}}" +
		"{{.}}\n" +
		"{{end}}"
)

func main() {

	outputmap := map[string]int{"bbb": 2, "aaa": 1, "ccc": 3}

	tw := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
	defer tw.Flush()

	t := template.Must(template.New("").Parse(ShowFormat))
	t.Execute(tw, outputmap)

}
1 Like

Hey @hjma29,

If you read the text/template package docs, you’ll see that for maps, if it’s keys are able to be sorted, it will be:

{{range pipeline}} T1 {{end}}
	The value of the pipeline must be an array, slice, map, or channel.
	If the value of the pipeline has length zero, nothing is output;
	otherwise, dot is set to the successive elements of the array,
	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.

You can see it on it’s package docs page here: https://golang.org/pkg/text/template/#hdr-Actions

You can always enforce a certain order by using a slice if you’d like.

3 Likes

One other thing if you actually want to use a map and keep it random, is you can just create a function that outputs the map and then use that function from within your template.

For example these results will be different now and then:

package main

import (
	"html/template"
	"log"
	"os"
	"strconv"
)

const (
	tmpl = `{{ randomMap . }}`
)

func randomMap(m map[string]int) string {
	output := ""
	for k, v := range m {
        output += fmt.Sprintf("%s: %d\n", k, v)
	}
	return output
}

func main() {
	m := map[string]int{
		"bbb": 2, "aaa": 1, "ccc": 3,
	}

	fncs := template.FuncMap{
		"randomMap": randomMap,
	}

	t := template.Must(template.New("").Funcs(fncs).Parse(tmpl))

	err := t.Execute(os.Stdout, m)
	if err != nil {
		log.Fatalln(err)
	}
}

(Just be aware if you try this in the Go playground, the results will stay the same because of caching, but locally the results will differ)

3 Likes

thank you @radovskyb!!

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.