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

1

6

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}}
Graphic answered 6/12, 2017 at 18:26 Comment(0)
B
6

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
Byre answered 6/12, 2017 at 18:43 Comment(2)
break and continue in range? Soon... (Q1 2022 if all goes well): https://mcmap.net/q/1777699/-how-to-use-continue-and-break-keywords-in-golang-templatesMccormack
@Mccormack Thanks for the note, will add it to the answer once it arrives :)Byre

© 2022 - 2024 — McMap. All rights reserved.