Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate over only the first n items of an array in a Go template

I have a vector with n elements. I'm using it to render items in a template. But I need to render only the 5 first elements. Please note that is possible to have less than 5 elements in vector, in this case will render all elements. Is there is a way to do it in template?

{{range .Categorias}}
    <li class="nav-item">
        {{.Nome}}
    </li>
{{end}}
like image 799
André G. Andrade Avatar asked Dec 06 '17 18:12

André G. Andrade


1 Answers

Easiest would be to only pass 5 elements, so you don't need any logic in the template.

You can also do this in template, if you also store the index in the {{range}} action. Then you can use an {{if}} action to check the index, and only render the body of the {{if}} if the index is less than 5:

{{range $i, $e := .Categorias}}{{if lt $i 5}}
    <li class="nav-item">
        {{.Nome}}
    </li>
{{end}}{{end}}

Here's a simple example demonstrating it:

func main() {
    t := template.Must(template.New("").Parse(src))
    numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
    if err := t.Execute(os.Stdout, numbers); err != nil {
        panic(err)
    }
}

const src = `{{range $i, $e := .}}{{if lt $i 5}}{{.}} {{end}}{{end}}`

Output (try it on the Go Playground):

0 1 2 3 4 

Go 1.10 template extension

Note that in Go 1.10 there will be new {{break}} and {{continue}} actions which will provide an alternative, better solution for this.

It will look something like this:

{{range $i, $e := .Categorias}}
    <li class="nav-item">
        {{.Nome}}
    </li>
{{if eq $i 4}}{{break}}{{end}}{{end}}

This new {{break}} action will provide a superior solution as the above {{range}} action will only iterate over 5 elements at most (while the other solution without {{break}} has to iterate over all elements, just elements with index >= 5 are not rendered).

Go 1.13 template extension

Go 1.13 adds the slice builtin template function which provides an even nicer and more efficient solution. Slice the value you want to range over, so the {{range}} action only "gets" what you want to loop over:

func main() {
    t := template.Must(template.New("").Parse(src))
    numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
    if err := t.Execute(os.Stdout, numbers); err != nil {
        panic(err)
    }
}

const src = `{{range $i, $e := (slice . 0 5)}}{{.}} {{end}}`

This will output (try it on the Go Playground):

0 1 2 3 4
like image 177
icza Avatar answered Oct 06 '22 11:10

icza