英文:
Check if value exists in nested array with template package
问题
使用text/template
,我需要找出数组中是否有任何对象具有特定的属性值。
假设我有一个人员列表(json):
[
{"name": "ANisus", "sex":"M"},
{"name": "Sofia", "sex":"F"},
{"name": "Anna", "sex":"F"}
]
使用模板,我希望得到以下输出:
女性:
Sofia
Anna
但是,只有在实际上有任何性别设置为F
的人员时,标题女性:
才会显示。在模板系统中如何实现这一点?我的第一次尝试是使用一个变量:
{{$hasFemale := 0}}
{{range .}}{{if eq .sex "F"}}{{$hasFemale := 1}}{{end}}{{end}}
{{if $hasFemale}}女性:{{end}}
我没有成功实现这个,因为范围内的$hasFemale处于不同的作用域,而不是在初始化为0的作用域中。我似乎找不到一种在初始化后更改变量的方法。
"工作"示例:http://play.golang.org/p/T-Ekx7n9YQ
而且我不能将这个逻辑移到应用程序中,它必须在模板内完成。
英文:
Using the text/template
, I need to find out if any object in an array has a certain property value.
Let's say I have a list of people (json):
[
{"name": "ANisus", "sex":"M"},
{"name": "Sofia", "sex":"F"},
{"name": "Anna", "sex":"F"}
]
Using the template, I want to have this output:
Females:
Sofia
Anna
But the header, Females:
, should only show in case there are actually any person with sex
set to F
. How can I perform this within the template system? My first attempt was by using a variable:
{{$hasFemale := 0}}
{{range .}}{{if eq .sex "F"}}{{$hasFemale := 1}}{{end}}{{end}}
{{if $hasFemale}}Female:{{end}}
I didn't get this to work because the $hasFemale within the range is in a different scope and not the same as the one initiated with 0. I can't seem to find a way to change a variable once it is initiated.
"Working" example: http://play.golang.org/p/T-Ekx7n9YQ
And I can not move this logic to the application; it must be done within the template.
答案1
得分: 5
我会以不同的方式解决这个问题。通常情况下,你不希望在模板中使用状态,它们应该保持简单。
由于你只想知道数据集中是否有女性,可以在你的数据上定义一个名为HasFemales
的方法,并从模板中调用它。你不一定需要定义一个结构体,你可以使用自定义类型和以下定义(在playground上修改后的示例):
type People []interface{}
func (p People) HasFemale() bool {
for _, v := range p {
if m, ok := v.(map[string]interface{}); !ok {
return false
} else if _, ok := m["sex"]; ok && m["sex"] == "F" {
return true
}
}
return false
}
然后你的模板将如下所示:
{{if .HasFemale}}Female:
{{range .}}{{if eq .sex "F"}}{{.name}}{{end}}{{end}}
{{end}}
当然,这种方式不像结构体那样类型安全,也不像结构体那样好用,所以我建议使用encoding/json
的反射功能将值映射到结构体,并在结构体上定义该方法。这样还有一个好处,就是能够在内部缓存.HasFemale
的结果,这样你就不需要每次都进行迭代。
关于你的评论的更新:
> 我有一个接受两个参数的Go应用程序:1)模板文件和2)JSON文件。它使用数据执行模板并输出文件。然后将输出文件传递给wkhtmltopdf生成PDF。每个模板/数据对都有我无法控制的任意数据结构,因此Go应用程序必须是通用的。
在这种情况下,将HasFemale
定义为通用的。这与你在问题中所做的类似,但它的好处在于抽象了范围的嵌套,使你能够将结果存储在一个变量中,并在整个模板中保持状态。示例:
type Data []interface{}
func (p Data) HasField(name string, value interface{}) bool {
for _, v := range p {
if m, ok := v.(map[string]interface{}); !ok {
return false
} else if _, ok := m[name]; ok && reflect.DeepEqual(m[name], value) {
return true
}
}
return false
}
示例用法:
{{$hasFemale := .HasField "sex" "F"}}
{{if $hasFemale}}Female:
{{range .}}{{if eq .sex "F"}}{{.name}}{{end}}{{end}}
{{end}}`
英文:
I would solve this differently. You generally don't want state in templates, they ought to be simple.
As you only want to know whether or not there are females in your data set, define a method HasFemales
on your data and call it from the template. You don't necessarily need to define a struct, you can use a custom type and a definition like this (your modified example on play):
type People []interface{}
func (p People) HasFemale() bool {
for _, v := range p {
if m, ok := v.(map[string]interface{}); !ok {
return false
} else if _, ok := m["sex"]; ok && m["sex"] == "F" {
return true
}
}
return false
}
Your template would then look like this:
{{if .HasFemale}}Female:
{{range .}}{{if eq .sex "F"}}{{.name}}{{end}}{{end}}
{{end}}
This is, of course, not as type safe as a struct and not as nice as a struct so I recommend
using the encoding/json
reflection feature to map values to a struct and define the method on that. This also has the benefit of being able to cache the result of .HasFemale
internally so you don't need to iterate every time.
Update regarding your comment:
> I have my Go application that takes two arguments: 1) a template file and 2) a json file. It executes the template with the data and outputs the file. The output file is then passed on to wkhtmltopdf to generade a pdf. Each template/data pair have arbitrary data structures beyond my control, so the Go application must be generic
In this case, make HasFemale
generic. This is similar to what you did in your question but has the benefit to abstract the nesting of the range, making it possible for you to store the result in a variable and have the state throughout your template. Example:
type Data []interface{}
func (p Data) HasField(name string, value interface{}) bool {
for _, v := range p {
if m, ok := v.(map[string]interface{}); !ok {
return false
} else if _, ok := m[name]; ok && reflect.DeepEqual(m[name], value) {
return true
}
}
return false
}
Example usage:
{{$hasFemale := .HasField "sex" "F"}}
{{if $hasFemale}}Female:
{{range .}}{{if eq .sex "F"}}{{.name}}{{end}}{{end}}
{{end}}`
答案2
得分: 1
我知道这是一个旧问题,但是我最近遇到了一个类似的用例,我需要根据范围内是否存在某个字段来使HTML表格中的一列变为可选。
原始的提问者已经接近成功了,只是在使用go语言时遇到了一个常见的问题,他们在错误的作用域中设置了变量。
所以,不要使用{{$hasFemale := 1}}
这样在if语句中设置变量,而应该写成{{$hasFemale = 1}}
,这样它就能按照他们最初的写法正常工作。
{{$hasFemale := 0}}
{{range .}}{{if eq .sex "F"}}{{$hasFemale = 1}}{{end}}{{end}}
{{if $hasFemale}}Female:{{end}}
这是他们原始示例的修复版本:https://play.golang.org/p/m_RPJmqsuet
英文:
I know this is an old question, however I just had a similar use case where I needed to make a column in an html table optional dependant on if a field exists anywhere within the range.
The original poster was almost there, they just had a frequent gotcha with go and was setting the variable in the wrong scope.
So instead of {{$hasFemale := 1}}
which set the variable within the if, they should have written it as {{$hasFemale = 1}}
then it would have worked as they originally written it.
{{$hasFemale := 0}}
{{range .}}{{if eq .sex "F"}}{{$hasFemale = 1}}{{end}}{{end}}
{{if $hasFemale}}Female:{{end}}
Here's their original example with the fix https://play.golang.org/p/m_RPJmqsuet
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论