Iterate Go map get index

做~自己de王妃 提交于 2019-12-10 15:45:13

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!