I am new to Go and I am struggling trying to figure out a way to return unique variables from an array in Go templating language. This is to configure some software and I do not have access to the source to change the actual program only the template.
I have knocked up an example in the Go playground:
package main
import "os"
import "text/template"
func main() {
var arr [10]string
arr[0]="mice"
arr[1]="mice"
arr[2]="mice"
arr[3]="mice"
arr[4]="mice"
arr[5]="mice"
arr[6]="mice"
arr[7]="toad"
arr[8]="toad"
arr[9]="mice"
tmpl, err := template.New("test").Parse("{{range $index, $thing := $}}The thing is: {{$thing}}
{{end}}")
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, arr)
if err != nil { panic(err) }
}
Right now this returns:
The thing is: mice
The thing is: mice
The thing is: mice
The thing is: mice
The thing is: mice
The thing is: mice
The thing is: mice
The thing is: toad
The thing is: toad
The thing is: mice
What I am trying to do is craft a template that from the input array filters duplicates and only returns:
The thing is: mice
The thing is: toad
I am really stuck as I know virtually no go and struggle to find any array manipulation methods in the docs. Any one have any tips?
Sorry for not being clear I wrote this question on the bus on the way to work.
I don't have access to any go code outside the template. I have a template I can edit and within that template I have an array that may or may not have multiple values and I need to print them once.
I appreciate this is not how templates are meant to work but if there is some dirty way to do this it would save me several days work.
You can create your own functions for the template via template.FuncMap
:
arr := []string{
"mice",
"mice",
"mice",
"mice",
"mice",
"mice",
"mice",
"toad",
"toad",
"mice",
}
customFunctions := template.FuncMap{"unique" : unique}
tmpl, err := template.New("test").Funcs(customFunctions).Parse("{{range $index, $thing := unique $}}The thing is: {{$thing}}
{{end}}")
Where unique
is defined as:
func unique(e []string) []string {
r := []string{}
for _, s := range e {
if !contains(r[:], s) {
r = append(r, s)
}
}
return r
}
func contains(e []string, c string) bool {
for _, s := range e {
if s == c {
return true
}
}
return false
}
Output:
The thing is: mice
The thing is: toad
(It might be better to use a map
.. but this gives you the basic idea)
That said - have you considered filtering this outside of the template? That would make things nicer for you.. then you can just iterate over the actual slice within the template.
Working sample: https://play.golang.org/p/L_8t10CpHW
The template can acces a particular custom function http://golang.org/pkg/text/template/#FuncMap. This allows your own logic to be called from within the template.
There is a comprehensive example in the docs which I wont repeat here. The key line is setting up a funcion Map and providing this to the template:
tmpl, err := template.New("titleTest").Funcs(funcMap).Parse(templateText)
Then can be accessed within the template.
{{myCustomFuction .}}
Since now it is established that it will take more go code in the form of a mapped function to achieve, I thought I would share a thought that might get the job done. If you have control of which template that the 'go' program you are not able to modify runs, then you could make several passes. I am also assuming you are on linux. Something like this:
goexe 'first template' // this writes to 'text file'
cat textfile | sort | uniq > 'text file'
goexe 'second template' // your desired output