How to compare Map for common keys and print the output? - go

I have the following code which generates the following output
code:
package main
import (
"html/template"
"os"
)
type EntetiesClass struct {
Name string
Value int32
}
// In the template, we use rangeStruct to turn our struct values
// into a slice we can iterate over
var htmlTemplate = `{{range $index, $element := .}}
{{range $element}}{{.Name}}={{.Value}}
{{- end}}
{{- end}}`
func main() {
data := map[string][]EntetiesClass{
"Container1": {{"Test", 15}},
"Container2": {{"Test", 15}},
}
t := template.New("t")
t, err := t.Parse(htmlTemplate)
if err != nil {
panic(err)
}
err = t.Execute(os.Stdout, data)
if err != nil {
panic(err)
}
}
link: https://play.golang.org/p/yM9_wWmyLY
Output:
Test=15
Test=15
I want to compare the Container1 with Container2 and if they have common key, i just want to print output only once.
Output:
Test=15
How can i achieve this? Any help is appreciated?

I can think of two ways to do this:
Dedup your data before passing to the template execution
This means you can pre-process the data before passing to t.Execute to eliminate duplicates. You can do this using something like:
m := map[EntitiesClass]bool{}
for _, ... {
m[ec] = true
// Or maybe you want to aggregate "Container 1"
// and "Container 2" in some way
}
Then you can just pass the processed data and the template itself remains virtually unchanged
Add a custom function for your template
This means you can add a normal go function that receives as many EntitiesClass as you like and returns them deduplicated (perhaps with the mechanism from option 1).
You can even do something like:
{{if not customHaveSeenThisValueBefore }}
...
{{ endif }}
For your simple example I would choose option 1, it seems easiest to leave templates very simple.

This applies some filtering logic. I'm using text/template because this code isn't using a server. It will work the same with html/template. When appending to slices of structs pointers have to be used. If you don't know why see here. func isInSlice() bool needs to be modded to your needs. This serves as an example.
package main
import (
"os"
"text/template"
)
type Entitie struct {
Results []*Data
}
type Data struct {
Name string
Value int32
}
// Change this function to mod how to filter
func (e *Entitie) isInSlice(name string) bool {
for _, item := range e.Results {
if item.Name == name {
return true
}
}
return false
}
func (e *Entitie) AddData(name string, value int32) {
if !e.isInSlice(name) {
e.Results = append(e.Results, &Data{Name: name, Value: value})
}
}
// In the template, we use rangeStruct to turn our struct values
// into a slice we can iterate over
var template = `
{{range $i, $e := .Data.Results}}
{{$e.Name}} = {{$e.Value}}
{{end}}
`
func main() {
data := make(map[string]Entitie)
var entities Entitie
entities.AddData("test", 15)
entities.AddData("test", 15)
entities.AddData("test2", 15)
t := template.New("t")
t, err := t.Parse(template)
if err != nil {
panic(err)
}
data["Data"] = entities
err = t.Execute(os.Stdout, data)
if err != nil {
panic(err)
}
}

Related

How to omit empty json fields using json.decoder

I try to understand why both functions return the same output.
As far as I understood, the point of omit empty is to not add that key to the result struct.
I wrote this example, I was expecting the first output not to have the "Empty" key, but for some reason its value still shows as 0.
package main
import (
"encoding/json"
"fmt"
"strings"
)
type agentOmitEmpty struct {
Alias string `json:"Alias,omitempty"`
Skilled bool `json:"Skilled,omitempty"`
FinID int32 `json:"FinId,omitempty"`
Empty int `json:"Empty,omitempty"`
}
type agent struct {
Alias string `json:"Alias"`
Skilled bool `json:"Skilled"`
FinID int32 `json:"FinId"`
Empty int `json:"Empty"`
}
func main() {
jsonString := `{
"Alias":"Robert",
"Skilled":true,
"FinId":12345
}`
fmt.Printf("output with omit emtpy: %v\n", withEmpty(strings.NewReader(jsonString)))
// output with omit emtpy: {Robert true 12345 0}
fmt.Printf("output regular: %v\n", withoutEmpty(strings.NewReader(jsonString)))
// output without omit: {Robert true 12345 0}
}
func withEmpty(r *strings.Reader) agentOmitEmpty {
dec := json.NewDecoder(r)
body := agentOmitEmpty{}
err := dec.Decode(&body)
if err != nil {
panic(err)
}
return body
}
func withoutEmpty(r *strings.Reader) agent {
dec := json.NewDecoder(r)
body := agent{}
err := dec.Decode(&body)
if err != nil {
panic(err)
}
return body
}
You need to define Empty as *int so it will be replaced with nil when there is no value. Then it will not be saved in the database.

Using function instead of method in template

I use the following code
var data struct {
File FZR
API API
}
const tmpl = `
{{- range .File.Modules}}
# in context of {{.Name}}
echo {{.EchoText}}
{{end}}`
func EchoText(m mts) string {
return m.Type
}
and like this its not working, now I change it to
func (m mts) EchoText() string {
return m.Type
}
it will work but I want to make it work with the first option, how can I do it ?
I mean update the template ...
update: as approva answer https://golang.org/pkg/text/template/#example_Template_func
but there is only string, how can I register the EchoText
As #mkopriva suggested in his first comment you just have to reference all your functions into a template.FuncMap where you establish the mapping from names to functions
Then into the template you just need to call them passing them the mts as parameter.
package main
import (
"log"
"os"
"text/template"
)
type mts struct {
Type1 string
Type2 string
}
func main() {
funcMap := template.FuncMap{
"myFunc1": EchoType1,
"myFunc2": EchoType2,
}
const templateText = `
Input: {{printf "%q" .}}
Output1:{{myFunc1 .}}
Output2:{{myFunc2 .}}
`
tmpl, err := template.New("myFuncTest").Funcs(funcMap).Parse(templateText)
if err != nil {
log.Fatalf("parsing: %s", err)
}
myMts := mts{Type1: "myType1", Type2: "myType2"}
err = tmpl.Execute(os.Stdout, myMts)
if err != nil {
log.Fatalf("execution: %s", err)
}
}
func EchoType1(m mts) string {
return m.Type1
}
func EchoType2(m mts) string {
return m.Type2
}
This will produce the following output :
Input: {"myType1" "myType2"}
Output1:myType1
Output2:myType2

How can I use division in a golang template?

How can I use division in a golang template. I need to divide Id by 2.
For example
{{if .Id/2}}
HEY, I CAN DO IT!
{{else}}
WHY???
{{end}}
The package text/template (and subsequently html/template) can provide the functionality by defining division as a function using Template.Funcs:
func (t *Template) Funcs(funcMap FuncMap) *Template
In your case, a FuncMap with a divide function could look something like this:
fm := template.FuncMap{"divide": func(a, b int) int {
return a / b
}}
Full example (but without me trying to understand what you mean with if a/2):
package main
import (
"os"
"text/template"
)
func main() {
fm := template.FuncMap{"divide": func(a, b int) int {
return a / b
}}
tmplTxt := `{{divide . 2}}`
// Create a template, add the function map, and parse the text.
tmpl, err := template.New("foo").Funcs(fm).Parse(tmplTxt)
if err != nil {
panic(err)
}
// Run the template to verify the output.
err = tmpl.Execute(os.Stdout, 10)
if err != nil {
panic(err)
}
}
Output:
5
Playground: http://play.golang.org/p/VOhTYbdj6P

How do I print return value of a function?

Here is my function definition which returns a string
"addClassIfActive": func(tab string, ctx *web.Context) string
I'm trying to print it like this:
<a href="/home/"{{ printf "%s" addClassIfActive "home" .Context }}>Home</a>
http response is getting terminated when I'm trying to print.
What am I doing wrong?
Returning a boolean, and then using if works, still I'm curious how to print string returned from a function
The problem you have is that "home" and .Context will be the 3:rd and 4:th argument of printf and not the arguments of addClassIfActive. The return value of addClassIfActive becomes the 2:nd argument for printf.
But the solution is simple: you don't have to use printf to print.
If your function just returns a string, you can simply print it by writing:
{{addClassIfActive "home" .Context}}
Full working example:
package main
import (
"html/template"
"os"
)
type Context struct {
Active bool
}
var templateFuncs = template.FuncMap{
"addClassIfActive": func(tab string, ctx *Context) string {
if ctx.Active {
return tab + " content"
}
// Return nothing
return ""
},
}
var htmlTemplate = `{{addClassIfActive "home" .Context}}`
func main() {
data := map[string]interface{}{
"Context": &Context{true}, // Set to false will prevent addClassIfActive to print
}
// We create the template and register out template function
t := template.New("t").Funcs(templateFuncs)
t, err := t.Parse(htmlTemplate)
if err != nil {
panic(err)
}
err = t.Execute(os.Stdout, data)
if err != nil {
panic(err)
}
}
Output:
home content
Playground
You can't call functions in templates.
What you can do is use FuncMaps:
templates.go
var t = template.New("base")
// ParseFiles or ParseGlob, etc.
templateHelpers := template.FuncMap{
"ifactive": AddClassIfActive,
}
t = t.Funcs(templateHelpers)
your_template.tmpl
...
<span class="stuff">{{ if eq .Context | ifactive }} thing {{ else }} another thing {{ end }}</span>
...
I haven't tested this exact syntax, but I am using FuncMaps elsewhere. Make sure to read the better docs at text/template on FuncMaps for more examples.

Variable modification in golang [Mutability]

The below code opens up a .txt file and counts the word frequencies. I am following a book and I got confused:
My question is here:
filename := os.Args[1]
frequencyForWord := map[string]int{}
updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)
I create a variable called frequencyForWord and pass it into a function that does not return anything called func updateFrequencies
This function modifies the variable and that's why when I do fmt.Println(frequencyForWord) it shows me a map that has words as keys and their counts as values.
My question is:
why don't I have to do something like this
frequencyForWord = updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)
// And then change func updateFrequencies to something to returns a map
I thought in order for a function to modify a variable I need to pass in the variable as a reference like this updateFrequencies(filename, &frequencyForWord)
Original Code:
package main
import(
"fmt"
"path/filepath"
"os"
"log"
"bufio"
"strings"
"unicode"
)
func main() {
if len(os.Args) == 1 || os.Args[1] == "-h" {
fmt.Printf("usage: %s <file>\n", filepath.Base(os.Args[0]))
os.Exit(1)
}
filename := os.Args[1]
frequencyForWord := map[string]int{}
updateFrequencies(filename, frequencyForWord)
fmt.Println(frequencyForWord)
}
func updateFrequencies(filename string, frequencyForWord map[string]int) string {
file, err := os.Open(filename)
if err != nil {
log.Printf("Failed to open the file: %s.", filename)
}
defer file.Close()
readAndUpdateFrequencies(bufio.NewScanner(file), frequencyForWord)
}
func readAndUpdateFrequencies(scanner *bufio.Scanner, frequencyForWord map[string]int) {
for scanner.Scan() {
for _, word := range SplitOnNonLetter(strings.TrimSpace(scanner.Text())) {
frequencyForWord[strings.ToLower(word)] += 1
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
func SplitOnNonLetter(line string) []string {
nonLetter := func(char rune) bool { return !unicode.IsLetter(char) }
return strings.FieldsFunc(line, nonLetter)
}
Because the map structure doesn't contain the values itself but points to the structures holding the values.
As written in the documentation :
Like slices, maps hold references to an underlying data structure. If
you pass a map to a function that changes the contents of the map, the
changes will be visible in the caller.
That's just like when you pass a pointer to a function : it lets the function change your value.
Here's another example of the same phenomenon :
type A struct {
b *B
}
type B struct {
c int
}
func incr(a A) {
a.b.c++
}
func main() {
a := A{}
a.b = new(B)
fmt.Println(a.b.c) // prints 0
incr(a)
fmt.Println(a.b.c) // prints 1
}
The function is not modifying the variable, but the value bound to the variable. That's possible because a map is a mutable data structure and passing it to a function does not copy the structure. (A map is implicitly a reference to a hash table and the reference is passed around.)

Resources