Adding a slice to a slice, as a single item

Currently trying to make a nice gallery of all my photos. In doing so, I want to take a slice that contains all images, and turn it into a slice of slices, with each of those nested slices containing up to three images, representing one row of photos in my table.

(Using flexboxes is not an option, there are reasons behind my madness.)

Currently I have this code:

{{- define "_partials/subdivide-list.html"}}
    {{ $input := .list}}
    {{ $itemsPerRow := .itemsPerRow }}

    {{ $output := slice}}

    {{/* Maths in Hugo is too much of a pain for me right now, so we'll just do what is effectively an endless loop and bail
       * when done. This will obviously break if you have more than 10000000 * $itemsPerRow images but I'd say, don't worry about it.
       */}}
    {{ range 10000000}}
        {{ if gt (len $input) $itemsPerRow }}
            {{ $row := first $itemsPerRow $input }}
            {{ $input = after $itemsPerRow $input }}
            {{ $output = $output | append $row }}
        {{ else if eq (len $input) 0 }}
            {{/* avoid having a last "slice" that has zero items */}}
            {{ break }}
        {{ else }}
            {{ $output = $output | append $input }}           
            {{ break }}
        {{ end }}
    {{ end }}

    {{ return $output }}

{{- end}}

The problem is here:

            {{ $output = $output | append $row }}

$row, in this context, is a slice, as is $output.

The problem here is that append is overloaded with the following overloads:

collections.Append ELEMENT [ELEMENT...] COLLECTION
collections.Append COLLECTION1 COLLECTION2

As both $output and $row are collections, the latter overload triggers, and unfortunately, the semantics of that overload merge the two slices into one slice, rather than adding the first slice into the second as a single item. So I end up with a slice of images (which is identical to the $input parameter!) rather than a slice of slices of images.

The documentation at collections.Append has a couple of examples for adding a slice to a slice as a slice, but I am honestly stumped to figure out exactly how these examples even work, let alone how to adapt them to my usecase.

Any help?

See https://github.com/gohugoio/hugo/issues/11131.

For reference, here are the two related examples from the documentation:

If you start with a slice of a slice:

{{ $s := slice (slice "a" "b") }}
{{ $s }} → [[a b]]

{{ $s = $s | append (slice "c" "d") }}
{{ $s }} → [[a b] [c d]]

To create a slice of slices, starting with an empty slice:

{{ $s := slice }}
{{ $s }} → []

{{ $s = $s | append (slice (slice "a" "b")) }}
{{ $s }} → [[a b]]

{{ $s = $s | append (slice "c" "d") }}
{{ $s }} → [[a b] [c d]]

The first example, starting with a slice of a slice, is straightforward.

But the more common use case is starting with an empty slice.

This is a contrived example of creating a slice of slices, starting with an empty slice, then appending to it in a loop:

{{ $letters := slice "a" "b" "c" "d" "e" "f" }}

{{ $pairs := slice }}
{{ range seq 0 2 (sub (len $letters) 1) }}
  {{ if not . }}
    {{ $pairs = $pairs | append (slice (slice (index $letters .) (index $letters (add 1 .)))) }}
  {{ else }}
    {{ $pairs = $pairs | append (slice (index $letters .) (index $letters (add 1 .))) }}
  {{ end }}
{{ end }}
{{ $pairs }} → [[a b] [c d] [e f]]

The construct is goofy, having to do something different in the first iteration of the loop.

The workaround I ended up at was to add a “dummy slice” to the list before entering the loop, then removing it:

{{- define "_partials/subdivide-list.html"}}
    {{ $input := .list}}
    {{ $itemsPerRow := .itemsPerRow }}

    {{/* Semantics for Collection.append are different when working on empty vs. non-empty collections. 
       * See https://discourse.gohugo.io/t/adding-a-slice-to-a-slice-as-a-single-item/56628/2
       * As a workaround, we're adding a "sacrificial" zeroth item to the start of the list and then remove
       * it before the return statement.
       */}}
    {{ $output := slice $input }}

    {{/* Maths in Hugo is too much of a pain for me right now, so we'll just do what is effectively an endless loop and bail
       * when done. This will obviously break if you have more than 10000000 * $itemsPerRow images but I'd say, don't worry about it.
       */}}
    {{ range 10000000 }}
        {{ if gt (len $input) $itemsPerRow }}
            {{ $row := first $itemsPerRow $input }}
            {{ $input = after $itemsPerRow $input }}
            {{ $output = $output | append $row }}
        {{ else if eq (len $input) 0 }}
            {{/* avoid having a last "slice" that has zero items */}}
            {{ break }}
        {{ else }}
            {{ $output = $output | append $input }}           
            {{ break }}
        {{ end }}
    {{ end }}

    {{ $output = after 1 $output }}
    {{ return $output }}
{{- end}}

I’m not proud of that solution, but uh. I don’t want to put more branches into the loop. Can’t say I’m a big fan of this particular API decision, really.

edit: my gallery is now completed, see here for complete code: Adding a gallery to a Hugo blog | Hit To Key

1 Like