detect last item inside an array using range inside go-templates
Asked Answered
H

2

11

This program outputs simply

1,4,2,

but I would like to print

1,4,2.

As you can see the comma is printed after each items of an array.

package main

import "os"
import "text/template"

func main() {
    params := map[string]interface{}{
        "items": [3]int{1, 4, 2},
    }
    tpl := "{{range $i, $el := .items}}{{$el}},{{end}}"
    lister, _ := template.New("foo").Parse(tpl)
    lister.Execute(os.Stdout, params)
}

Is there a way to change {{range $i, $el := .items}}{{$el}},{{end}} and be sure that last item will print "." instead of ","

Hydrography answered 29/4, 2018 at 9:13 Comment(1)
Possible duplicate of #10747554, #21306365Isogloss
P
16

You can use

tpl := "{{range $i, $el := .items}}{{if $i}},{{end}}{{$el}}{{end}}."

to achieve that. The trick is to emit the comma separator first, but not for the first item in the range.

Pester answered 29/4, 2018 at 9:31 Comment(2)
It makes me smile, but it works ^_^. I've not detected the last items, ... but solved the problem. Thank you very much ^_^Hydrography
If you do not want the trailing dot in the case of an empty list, then use this variation on the answer {{with .items}}{{range $i, $el := .}}{{if $i}},{{end}}{{$el}}{{end}}.{{end}}Isogloss
I
5

In the simple example you posted, easiest is what "fhe" posted: you can easily check if the current index is the first, and output a comma after non-first elements. And finally output a trailing dot.

If in a more complicated example you do need to detect the last item, or it may be that the list you iterate over may be empty (and thus the trailing dot would be a mistake), you may register a custom function to tell if the current index is the last:

lister := template.Must(template.New("foo").Funcs(template.FuncMap{
    "IsLast": func(i, size int) bool { return i == size-1 },
}).Parse(tpl))

And use the following template then:

tpl := "{{range $i, $el := .items}}{{$el}}{{if IsLast $i (len $.items)}}.{{else}},{{end}}{{end}}"

Then the output will be (try it on the Go Playground):

1,4,2.

A variation of this could be to register a custom function which calculates the last index from the length (lastIdx = length - 1), and then inside the {{range}} you can do a simple comparison:

tpl := "{{$lastIdx := LastIdx (len .items)}}{{range $i, $el := .items}}{{$el}}{{if eq $lastIdx $i}}.{{else}},{{end}}{{end}}"
lister := template.Must(template.New("foo").Funcs(template.FuncMap{
    "LastIdx": func(size int) int { return size - 1 },
}).Parse(tpl))

Output will be the same. Try it on the Go Playground.

Intarsia answered 29/4, 2018 at 9:37 Comment(2)
Instead of You inspired me!! Instead of create a function I can pass directly the number of item of the array: "{{range $i, $el := .items}}{{$el}}{{if eq $i $.last}}.{{else}},{{end}}{{end}}"Hydrography
@Hydrography Yes, that's even simpler for a single case. Might be more hassle and work if you need it in many places.Intarsia

© 2022 - 2024 — McMap. All rights reserved.