英文:
Last item in a template range
问题
给定模板:
{{range $i, $e := .SomeField}}
{{if $i}}, {{end}}
$e.TheString
{{end}}
这可以输出:
one, two, three
然而,如果我想输出:
one, two, and three
我需要知道上述范围中的最后一个元素。
我可以设置一个变量来保存数组.SomeField
的长度,但它将始终为3,并且上面的$i
值只能达到2。根据我所见,模板中不能进行算术运算。
在模板范围中检测最后一个值是否可能?谢谢。
英文:
Given the template:
{{range $i, $e := .SomeField}}
{{if $i}}, {{end}}
$e.TheString
{{end}}
This can output:
one, two, three
If, however, I want to output:
one, two, and three
I'd need to know which is the last element in the range above.
I can set a variable that holds the length of the array .SomeField
, but that will always be 3, and the $i
value above will only ever get to 2. And you can't perform arithmetic in templates from what I've seen.
Is detecting the last value in a template range possible? Cheers.
答案1
得分: 21
这可能不是最优雅的解决方案,但是这是我能找到的最好的解决方案:
package main
import (
"os"
"reflect"
"text/template"
)
var fns = template.FuncMap{
"last": func(x int, a interface{}) bool {
return x == reflect.ValueOf(a).Len() - 1
},
}
func main() {
t := template.Must(template.New("abc").Funcs(fns).Parse(`{{range $i, $e := .}}{{if $i}}, {{end}}{{if last $i $}}and {{end}}{{$e}}{{end}}.`))
a := []string{"one", "two", "three"}
t.Execute(os.Stdout, a)
}
**注意:**你也可以使用len
函数而不使用反射来实现(感谢Russ Cox):
http://play.golang.org/p/V94BPN0uKD
参考资料:
- https://groups.google.com/forum/#!topic/golang-nuts/yRXHSCjVAcM
- https://groups.google.com/forum/#!msg/golang-nuts/XBScetK-guk/Bh7ZFz6R3wQJ
- https://groups.google.com/forum/#!topic/golang-nuts/mqRbR7AFJj0
英文:
This is probably not the most elegant solution but it's the best I could find:
http://play.golang.org/p/MT91mLqk1s
package main
import (
"os"
"reflect"
"text/template"
)
var fns = template.FuncMap{
"last": func(x int, a interface{}) bool {
return x == reflect.ValueOf(a).Len() - 1
},
}
func main() {
t := template.Must(template.New("abc").Funcs(fns).Parse(`{{range $i, $e := .}}{{if $i}}, {{end}}{{if last $i $}}and {{end}}{{$e}}{{end}}.`))
a := []string{"one", "two", "three"}
t.Execute(os.Stdout, a)
}
Note: You can also do it without reflect using the len
function (credit to Russ Cox):
http://play.golang.org/p/V94BPN0uKD
c.f.
答案2
得分: 4
今天在使用docker inspect命令中处理格式时,我们遇到了同样的问题。
在不修改Docker的情况下,最简单的方法是(为了便于阅读,将表达式拆分成了多行):
{{ $image := "" }}
{{ range split .ContainerImageName "/" }}
{{ $image = . }}{{ end }}
{{ index (split $image ":") 0 }}
所以在我们的情况下,我们需要获取不带注册表地址和版本的镜像名称。
例如,像registry.domain.local/images/nginx:latest这样的镜像名称将变为nginx。
附注:您需要Go >= 1.11来完成此任务(https://github.com/golang/go/issues/10608)。
附注:问题是关于Go模板的,但对于那些在Docker中遇到相同问题的人,这里有一些配置示例:
1)在daemon.json
中使用Go模板
cat /etc/docker/daemon.json
{
"log-driver": "syslog",
"log-opts": {
"syslog-address": "udp://127.0.0.1:20627",
"tag": "{{ $image := \"\" }}{{ range split .ContainerImageName \"/\" }}{{ $image = . }}{{ end }}{{ index (split $image \":\") 0 }}/{{.Name}}"
}
2)使用-f
选项的Go模板:
docker inspect \
-f '{{ $image := "" }}{{ range split .Config.Image "/" }}{{ $image = . }}{{ end }}{{ index (split $image ":") 0 }}' \
<container>
英文:
We had same problem today when working with format in docker inspect command.
The easiest way to get the last element without patching Docker was (the expression has been split into lines for ease of reading):
{{ $image := "" }}
{{ range split .ContainerImageName "/" }}
{{ $image = . }}{{ end }}
{{ index (split $image ":") 0 }}
So in our case, we needed the image name without registry address and version.
For example, image name like registry.domain.local/images/nginx:latest becomes nginx.
P.S: You need Go >= 1.11 to do the job (https://github.com/golang/go/issues/10608)
P.P.S: The question was about Go template but for those who had same problems with Docker here the configuration examples:
- Using Go template in
daemon.json
cat /etc/docker/daemon.json
{
"log-driver": "syslog",
"log-opts": {
"syslog-address": "udp://127.0.0.1:20627",
"tag": "{{ $image := \"\" }}{{ range split .ContainerImageName \"/\" }}{{ $image = . }}{{ end }}{{ index (split $image \":\") 0 }}/{{.Name}}"
}
- Using Go template with
-f
option:
docker inspect \
-f '{{ $image := "" }}{{ range split .Config.Image "/" }}{{ $image = . }}{{ end }}{{ index (split $image ":") 0 }}' \
<container>
答案3
得分: 1
我遇到了这个问题,并通过添加一个isLast
辅助函数来解决它。它需要在辅助函数外计算长度,但阅读起来更容易一些。
var helpers template.FuncMap = map[string]interface{}{
"isLast": func(index int, length int) bool {
return index+1 == length
},
}
使用方法如下:
{{$lenMyList := len .MyList}}
{{range $index, $item := .MyList}}
<div>
{{$item.SomeItemProp}}
{{if (isLast $index $lenMyList)}}
最后一项
{{end}}
</div>
{{end}}
英文:
I ran into this and solved it by adding an isLast
helper function. It does require computing the length outside the helper, but it's a bit easier to read.
var helpers template.FuncMap = map[string]interface{}{
"isLast": func(index int, len int) bool {
return index+1 == len
},
}
Which is used like:
{{$lenMyList := len .MyList}}
{{range $index, $item := .MyList}}
<div>
{{.SomeItemProp}}
{{if (isLast $index $lenMyList)}}
Last Item
{{end}}
</div>
{{end}}
答案4
得分: 1
稍微更优雅的解决方案,避免在每次迭代中调用len
或sub
/add
:
package main
import (
"os"
"text/template"
)
var fns = template.FuncMap{
"eq": func(x, y interface{}) bool {
return x == y
},
"sub": func(y, x int) int {
return x - y
},
}
func main() {
t := template.Must(template.New("abc").Funcs(fns).Parse(`{{$last := (len . | sub 1)}}{{range $i, $e := .}}{{if $i}}, {{end}}{{if eq $i $last}}和{{end}}{{$e}}{{end}}。`))
a := []string{"one", "two", "three"}
t.Execute(os.Stdout, a)
}
链接:https://go.dev/play/p/oRznNPw-YCr
英文:
Slightly more elegant solution, avoids calling len
or sub
/add
on every iteration:
https://go.dev/play/p/oRznNPw-YCr
package main
import (
"os"
"text/template"
)
var fns = template.FuncMap{
"eq": func(x, y interface{}) bool {
return x == y
},
"sub": func(y, x int) int {
return x - y
},
}
func main() {
t := template.Must(template.New("abc").Funcs(fns).Parse(`{{$last := (len . | sub 1)}}{{range $i, $e := .}}{{if $i}}, {{end}}{{if eq $i $last}}and {{end}}{{$e}}{{end}}.`))
a := []string{"one", "two", "three"}
t.Execute(os.Stdout, a)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论