Append front matter array after slice

{{ $cuisines := slice }}
{{ range .RegularPages }}
  {{ $cuisines   = $cuisines   | append (.Params.cuisines  | default (slice)) }}
{{ end }}
{{ $cuisines = $cuisines | uniq }}

I am trying the above.
Let’s say the cuisines of my pages are:

Page1.md
cuisines: ["Indian"]

Page2.md
cuisines: ["Chinese"]

Page3.md
not set

Page4.md
cuisines: ["Greek"]

Page5.md
cuisines: ["German"]

Expected value of $cuisines => [“Indian”, “Chinese”, “Greek”, “German”]
Actual value of $cuisines => [“Indian”, “Chinese”, [“Greek”], [“German”]]

Somehow the add of the default slice in case of Page 3 is triggering the following to become nested? I’m not sure why this is happening. Has anybody faced this?

running Hugo v0.143.0 I am not able to reproduce your problem.

looks like:

  • either your definition of not set is kinda different
  • your frontmatter extract posted is not accurate.
  • you corrected the code while posting :wink:

please recheck and if you cannot solve it share your repo or at least your frontmatter of ALL 5 Pages unchanged


let’s say I have frontmatter for the pages where it is set as:

1,2,4,5:

---
"params": {
   "cuisines": [
      "COUNTRY"
   ]
}
---

page 3:

---
---

and your code:

  {{ $cuisines := slice }}
  {{/* need to use site. here cause I'm not in a bundle */}}
  {{ range site.RegularPages }}
    {{ $cuisines   = $cuisines   | append (.Params.cuisines  | default (slice)) }}
  {{ end }}
  {{ $cuisines = $cuisines | uniq }}
  {{ printf "%v" $cuisines }}
  {{ highlight ($cuisines | jsonify (dict "indent" "  ")) "JSON"}}

it will give me:

[Indian Chinese Greek German]

and

[
  "Indian",
  "Chinese",
  "Greek",
  "German"
]

p.s. works in YAML and TOML, too.

I’m using an older version of hugo (v0.118.0). Maybe this was a bug?
I can reproduce this without any frontmatter involved.

  {{ $a := slice 1 2 }}
  {{ $b := slice 4 3 }}
  {{ $c := slice }}
  {{ $a = $a | append $b  }}
  {{ $a = $a | append ($c | default (slice)) }}
  {{ $a = $a | append ($c | default (slice 43 12)) }}
  {{ $a = $a | append ($c | default (slice 4 12)) }}
  {{ $a = $a | append $b  }}

  <div>
    {{ $a }}
  </div>

What I expect to be printed: [1 2 4 3 43 12 4 12 4 3]
What is actually printed: [1 2 4 3 [43 12] [4 12] [4 3]]

I have copy pasted the code, and not corrected anything :slight_smile:

This one is not unset :wink: it’s an empty array dunno if your frontmatter looks like cuisines: []

but nevertheless good news:

I was able to reproduce that one in v0.118 :wink: AND with your new code in v0.143.0

after appending an empty array using {{ $a | append slice }} looks like next append thinks it has to add the rest as arrays, too.

I’ll need to dig a little into, but it could be a bug

in your special case you could swap the arguments doing {{ $a = ($c | default slice) | append $a }} but that looks weird. I also would suggest to skip that default stuff completely.

I think what you really want is:
the union of all the elements in cuisine without duplicates:

{{ define "main" }}
  {{ $cuisines := slice }}
  {{/* need to use site. here cause I'm not in a bundle */}}
  {{ range site.RegularPages }}
   {{ with .Params.cuisines }}
      {{ $cuisines   = union $cuisines . }}
   {{ end }}
  {{ end }}
  {{ printf "%v" $cuisines }}
  {{ highlight ($cuisines | jsonify (dict "indent" "  ")) "JSON"}}
{{ end }}

tracked that down a little and it looks the internal representation changes when appending an empty array:

appending an empty array

  {{ $a := slice 1 }}
  <div>{{ debug.Dump $a }}</div>
  {{ $a = $a | append slice }}
  <div>{{ debug.Dump $a }}</div>

[]int{ 1, }
[]interface {}{ 1, }

appending a non empty array

  {{ $a := slice 1 }}
  <div>{{ debug.Dump $a }}</div>
  {{ $a = $a | append (slice 2 )}}
  <div>{{ debug.Dump $a }}</div>

[]int{ 1, }
[]int{ 1, 2, }

swap the arguments

  {{ $a := slice 1}}
  <div>{{ debug.Dump $a }}</div>
  {{ $a = slice | append $a }}
  <div>{{ debug.Dump $a }}</div>

[]int{ 1, }
[]int{ 1, }

I must admit here I’m out maybe @jmooring could shed some light on that.

Assuming cuisines is a taxonomy, it seems like this would be a better way to get a slice of all the terms:

{{ $cuisines := slice }}
{{ range site.Taxonomies.cuisines }}
  {{ $cuisines = $cuisines | append .Page.Title }}
{{ end }}
{{ $cuisines }} → [Chinese German Greek Indian]

If you don’t want the global set of terms, but instead only those in the current page collection, use the GetTerms method:

{{ $cuisines := slice }}
{{ range .RegularPages }}
  {{ range .GetTerms "cuisines" }}
    {{ $cuisines = $cuisines | append .Page.Title }}
  {{ end }}
{{ end }}
{{ $cuisines | uniq | sort }} → [Chinese German Greek Indian]

In both examples above we get a page reference, so we can use the title of the term page instead of the term itself. That allows you, for example, to use a term like “chinese” but populate the slice with “Chinese Food”.

If cuisines is not a taxonomy:

{{ $cuisines := slice }}
{{ range .RegularPages }}
  {{ with .Params.cuisines }}
    {{ $cuisines = $cuisines | append . }}
  {{ end }}
{{ end }}
{{ $cuisines | uniq | sort }} → [Chinese German Greek Indian]

The append function has at least one open issue; it may or may not be related.
https://github.com/gohugoio/hugo/issues/11131

1 Like

Thx for having a look at that. - results in:

  • always check the validity (defined, empty)
  • and do not append empty arrays :wink:

<tl;tr;>

and that one:

{{ $cuisines   = union $cuisines . }}
{{ $cuisines | sort }} → [Chinese German Greek Indian]

I estimate your’s will be faster for large collections anything else here?


should have checked before… sounds related

seems to be another flaw where adding an empty slice turns the collections type → and after that adding more slices will create sub slices instead of expanded values.

result might lead to confusion and produce strange results when appending arrays:

WARN  .  8: empty array in the middle
WARN  .  8: {{ $s := slice }}                        : []interface {}  -> []
WARN  .  8: {{ $s = $s | append (slice 1 2) }}       : []int           -> [1,2]
WARN  .  8: {{ $s = $s | append (slice 3 4) }}       : []int           -> [1,2,3,4]
WARN  .  8: {{ $s = $s | append slice }}             : []interface {}  -> [1,2,3,4]              <--- here's the switch
WARN  .  8: {{ $s = $s | append (slice 5 6) }}       : []interface {}  -> [1,2,3,4,[5,6]]

# printed with {{ warnf ".%3d: %-40s : %-15T -> %s" $i CODE $s (debug.Dump $s | replaceRE `\s*` "")}}