Go语言中迭代map并获取索引

10
为了在模板中使用 reveleven 关键字,我想在使用 range 迭代时获取映射条目的索引。有办法做到这一点吗?
我的映射结构如下:
map[string][]string

2
你的映射键是像“foo”和“wtf”这样的字符串。什么是偶数字符串?那不是事情的运作方式。 - Volker
1
你必须使用 even 吗?还是可以使用 CSS 样式 (:nth-child(odd), :nth-child(even)) 并忘记 even - kennytm
@kennytm,这实际上解决了我的问题。如果您将其发布为答案,我会接受它,这样就可以解决问题了。 - DiCaprio
4个回答

14

通过循环遍历映射表实现索引的简单方法:

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")
}

并打印出:

a xx b 1 c 0
a gg b 2 c 1
Hello, playground

3

仅仅使用模板操作是不够的,但您可以注册一个提供必要帮助的函数。

您可以注册一个返回函数(闭包)的函数,每次调用时交替返回其返回值(就像“奇数”和“偶数”索引一样):

func isEven() func() bool {
    e := false
    return func() bool {
        e = !e
        return e
    }
}

我将其命名为isEven()以避免与ravel的even()冲突。使用它:

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}}`

输出结果(请在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}}操作中调用$e,如下所示:

const templ = `{{$e := isEven}}
{{- range $k, $v := . -}}
    [{{if call $e}}even{{else}}odd {{end}}] key={{$k}}; value={{$v}}
{{end}}`

以下是运行此代码(在Go Playground上)的输出结果:

[even] key=a; value=A
[odd ] key=b; value=B
[even] key=c; value=C
[odd ] key=d; value=D

技术细节

此模板的操作:

{{$e := isEven}}

创建名为$e的新模板变量,其值将是isEven()函数调用的结果(返回值)。isEven()返回一个函数值,一个具有访问类型为bool的本地变量e的闭包。当您稍后执行{{call $e}}时,您不是在调用isEven() Go函数,而是调用该函数返回的函数(闭包)并存储在$e中。该闭包引用了本地bool变量e,它直到可访问isEvent()返回的函数时才被释放。

因此,每当您执行{{call $e}}时,它会调用闭包,闭包“拥有”类型为bool的变量e,其值在这个$e的调用之间得以保留。

如果您在模板中再次调用isEvent,那么它将返回一个新函数(闭包),包装第一个isEvent()调用返回的闭包的第一个包装变量实例独立的新实例。


它能工作,但我很好奇,为什么每次调用isEven()时e没有被重置为false? - DiCaprio
2
@Mrlenny 添加了一个新的 Under the hood 部分来解释内部机制。 - icza

2
在Go中,地图条目没有索引;您无法从任何元素中获取索引。另外,每次使用range迭代映射时,您都会得到不同的顺序——这表明映射中没有索引概念。
索引仅与有序数据结构相关(例如数组、切片、列表等),而不是映射。请查看https://blog.golang.org/go-maps-in-action以获取更多详细信息。

-3
{{range $key, $element := .Map}}
  {{$index := index .Map $key}}
{{end}}

这里的$index和$element均为字符串/[]字符串。但是我想知道迭代次数的数量。 - DiCaprio
通过编辑,我得到了“error calling index: cannot index slice/array with type string”的错误。现在,在@keynnytm的帮助下,我已经解决了这个问题。 - DiCaprio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接