2
votes

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

1 Answers

3
votes

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: