Iterate Go map get index
You cannot do this with template actions alone, but you can register a function that provides the assistance you need.
You can register a function that returns a function (a closure) that alternates between the return value when called (just like βoddβ and βevenβ indexes alternate):
func isEven() func() bool {
e := false
return func() bool {
e = !e
return e
}
}
I named it isEven()
to avoid colliding with ravel even()
. Using it:
func main() {
t := template.Must(template.New("").Funcs(template.FuncMap{
"isEven": isEven,
}).Parse(templ))
m := map[string]string{
"a": "A", "b": "B", "c": "C", "d": "D",
}
if err := t.Execute(os.Stdout, m); err != nil {
panic(err)
}
}
const templ = `{{$e := isEven}}
{{- range $k, $v := . -}}
[even:{{call $e}}] key={{$k}}; value={{$v}}
{{end}}`
Conclusion (try it on the Go Playground ):
[even:true] key=a; value=A
[even:false] key=b; value=B
[even:true] key=c; value=C
[even:false] key=d; value=D
If you need different output for odd and even iterations, you can call $e
in action {{if}}
, for example:
const templ = `{{$e := isEven}}
{{- range $k, $v := . -}}
[{{if call $e}}even{{else}}odd {{end}}] key={{$k}}; value={{$v}}
{{end}}`
Result of this (try it on the Go Playground ):
[even] key=a; value=A
[odd ] key=b; value=B
[even] key=c; value=C
[odd ] key=d; value=D
Under the hood
This is the template action:
{{$e := isEven}}
Creates a new template variable with a name $e
, and its value will be the result (return value) of the function call isEven()
. isEven()
returns the value of a function, a closure that has access to a local variable of the e
type bool
. When you are done {{call $e}}
, you are not calling the isEven()
Go function , but the function it returns (the closure) and saved to $e
. This closure has a reference to a local variable bool
e
, it is not "released" until the return function is available isEvent()
.
That way, whenever you do {{call $e}}
, it calls a closure that has a "has" e
type variable bool
whose value persists between calls to that $e
.
If you call it again isEvent
in the template, you will return a new function (closure) by wrapping a new instance of a local variable e
, independent of the first wrapped closure variable returned by the first call isEvent()
.
source to share
Map entries have no Go index; you cannot get the index from any element. Also, every time you iterate on a map with range
, you end up with a different order - another hint of the lack of an index concept on maps.
Indexes only refer to ordered data structures (such as arrays, fragments, lists, etc.), not maps. Take a look at https://blog.golang.org/go-maps-in-action for more details.
source to share
An easy way to reach the index while walking through the map:
package main
import (
"fmt"
)
func main() {
mm := map[string]int{"xx" : 1, "gg" : 2}
cnt := 0
for a, b:= range mm{
fmt.Println("a", a, "b",b, "c" , cnt)
cnt++
}
fmt.Println("Hello, playground")
}
And prints:
a xx b 1 c 0
a gg b 2 c 1
Hello, playground
source to share