Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate Go map get index

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
like image 300
DiCaprio Avatar asked Mar 22 '17 10:03

DiCaprio


People also ask

How do you check for the existence of a key in a map in Go?

When we execute the call to mymap['key'] we get back two distinct values, the first of which is the value of the key and the second is a bool value which represents whether or not the given key exists within the map. This second value is what we use to check if a given key exists in the if statement on line 13.


2 Answers

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
like image 176
EdwinGuo Avatar answered Sep 30 '22 12:09

EdwinGuo


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.

like image 32
icza Avatar answered Sep 30 '22 12:09

icza