问题
In order to use revel
's even keyword in templates I would like to get the index of a map entry when iterating with range
. Is there any way to do so?
My map has the structure:
map[string][]string
回答1:
You can't do this only with template actions, but you may register a function which provides the necessary help.
You may register a function which returns a function (closure), which alternates its return value whenever called (exactly how "odd" and "even" indices alternate):
func isEven() func() bool {
e := false
return func() bool {
e = !e
return e
}
}
I named it isEven()
to not collide with ravel's 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}}`
Output (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 want different output for odd and even iterations, you can call $e
in an {{if}}
action, like this:
const templ = `{{$e := isEven}}
{{- range $k, $v := . -}}
[{{if call $e}}even{{else}}odd {{end}}] key={{$k}}; value={{$v}}
{{end}}`
Output 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 template action:
{{$e := isEven}}
Creates a new template variable named $e
, and its value will be the result (return value) of the isEven()
function call. isEven()
returns a function value, a closure that has access to a local variable e
of type bool
. When later you do {{call $e}}
, you're not calling the isEven()
Go function, but the function it returned (the closure) and is stored in $e
. That closure has a reference to the local bool
variable e
, it is not "freed" until the function returned by isEvent()
is accessible.
So whenever you do {{call $e}}
, it calls the closure, which "has" an e
variable of type bool
, whose value is retained between calls of this $e
.
If you would call isEvent
in the template again, that would return a new function (closure), wrapping a new instance of the local variable e
, being independent of the first wrapped variable of the closure returned by the first isEvent()
call.
回答2:
A Simple way to achieve index while looping through a 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
回答3:
Map entries have no index in Go; there's no way you can get an index out of any element. Also, each time you iterate on a map with range
, you get a different order - another hint that there's no index concept in maps.
Indexes are related to ordered data structures only (e.g. arrays, slices, lists, etc), not maps. Take a look at https://blog.golang.org/go-maps-in-action for more details.
回答4:
{{range $key, $element := .Map}}
{{$index := index .Map $key}}
{{end}}
来源:https://stackoverflow.com/questions/42948556/iterate-go-map-get-index