英文:
Iterate through map in Go text template
问题
我有一个值的map
,看起来像这样:
vals := map[string]interface{}{"foo": 1, "bar": 2, "baz": 7}
data := map[string]interface{}{"bat": "obj", "values": vals}
为了生成以下字符串,我的模板应该是什么样的(注意正确的逗号使用)?
SET obj.foo=1, obj.bar=2, obj.baz=7
我从以下模板开始:
SET {{range $i, $v := .values}} {{.bat}}.{{$i}}={{$v}},{{end}}
但它只会打印出:
SET
而且即使它能工作,逗号也是不正确的。然后我尝试使用自定义函数来格式化该map
,但我无法让模板调用我的函数。以下任何一种似乎都不起作用:
SET {{.MyFunction .values}}
SET {{call .MyFunction .values}}
SET {{call MyFunction .values}}
当MyFunction
被定义为:
func MyFunction(data map[string]interface{}) string {
fmt.PrintLn('i was called!')
return "foo"
}
我使用一个辅助函数来执行模板,它看起来像这样:
func useTemplate(name string, data interface{}) string {
out := new(bytes.Buffer)
templates[name].Execute(out, data)
return string(out.Bytes())
}
谢谢!
英文:
I have a map
of values that looks like this:
vals := map[string]interface{}{"foo": 1, "bar": 2, "baz": 7}
data := map[string]interface{}{"bat": "obj", "values": vals}
What should my template look like to generate the following string (note the correct comma usage)?
SET obj.foo=1, obj.bar=2, obj.baz=7
I started with this as my template:
SET {{range $i, $v := .values}} {{.bat}}.{{$i}}={{$v}},{{end}}
But that just prints out
SET
And even if that did work, the commas would be incorrect. I then tried to use a custom function to format the map, but I couldn't get the template to ever call my function. None of the following seemed to work:
SET {{.MyFunction .values}}
SET {{call .MyFunction .values}}
SET {{call MyFunction .values}}
when MyFunction was defined as:
func MyFunction(data map[string]interface{}) string {
fmt.PrintLn('i was called!')
return "foo"
}
And I'm executing the templates using a helper function that looks like this:
func useTemplate(name string, data interface{}) string {
out := new(bytes.Buffer)
templates[name].Execute(out, data)
return string(out.Bytes())
}
Thanks!
答案1
得分: 3
这将让你接近目标:
SET {{range $key, $value := $.values}}{{$.bat}}.{{$key}}={{$value}} {{end}}
渲染结果为:
SET obj.bar=2 obj.baz=7 obj.foo=1
不幸的是,由于range
操作在映射上的迭代方式(没有数值索引),我认为没有简单的方法在值之间添加逗号。尽管如此,template
包的设计初衷是为了方便扩展,这样你就可以在模板中减少逻辑,将更多的逻辑放在Go代码中,因此很容易在Go中编写一个辅助函数,并将其提供给模板使用。
如果你愿意额外努力,那么模板会变得更简单,而且效率更高。辅助函数可以像这样:
func commaJoin(prefix string, m map[string]interface{}) string {
var buf bytes.Buffer
first := true
for k, v := range m {
if !first {
buf.WriteString(", ")
}
first = false
buf.WriteString(prefix)
buf.WriteByte('.')
buf.WriteString(k)
buf.WriteByte('=')
buf.WriteString(fmt.Sprint(v))
}
return buf.String()
}
然后你的模板将如下所示:
SET {{$.values | commaJoin $.bat}}
这里有一个使用这个逻辑的可工作示例:
英文:
This will get you pretty close:
SET {{range $key, $value := $.values}}{{$.bat}}.{{$key}}={{$value}} {{end}}
rendering as:
SET obj.bar=2 obj.baz=7 obj.foo=1
Unfortunately, I don't think there's any simple way to have the commas added in between the values due to how the range
action iterates on maps (there's no numeric index). That said, the template
packages were meant to be easily extensible so you can have less logic in your templates and more logic in Go itself, so it's easy enough to code a helper function in Go and make it available to your templates.
If you're happy to go that extra mile, then the template becomes much simpler, and also more efficient. The function can look like this:
func commaJoin(prefix string, m map[string]interface{}) string {
var buf bytes.Buffer
first := true
for k, v := range m {
if !first {
buf.WriteString(", ")
}
first = false
buf.WriteString(prefix)
buf.WriteByte('.')
buf.WriteString(k)
buf.WriteByte('=')
buf.WriteString(fmt.Sprint(v))
}
return buf.String()
}
and your template would look like:
SET {{$.values | commaJoin $.bat}}
Here is a working example with this logic:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论