Sorting Page collection by RE match of frontmatter Param

Hello all!

I have a collection of Pages whose frontmatter includes a key like:

ages: '5-10'
ages: '10-13'
ages: '8-11'

I’d like to range over the Pages but sort it by the first number in that Param (5, 8, 10 in my example).

I was trying to use an REmatch to grab only the first digits in that string to use to sort:

{{ $post := where $.Page.Pages "Params.duration" "day" }}
{{ $sorted := sort $post (index ( ".Params.ages" | findRE "^\\d+") 0) }}

  {{ range $sorted }}
    {{ index (.Params.ages | findRE "^\\d+") 0 }} :: {{ .Params.ages }}<br/>
  {{ end }}

But the output looks like:

8 :: 8-11
10 :: 10-13
5 :: 5-8
10 :: 10-13

It seems like this should work, but I’m not sure why it’s not. Any ideas?

I’d use min-age and max-age parameters instead. Why stuff numbers into a string if you have to convert that string later on back to numbers?

Also, you should make sure that you’re comparing numbers, not strings.

I considered that, but I already have this key and use it in other places as-is.

I figured the logic would work the same, as the first digits in the string represent the youngest age to sort by.

https://gohugo.io/functions/collections/sort

The second arg is a key, not a value.

the easiest would be @chrillek s suggestion to declare an int in your frontmatter. (maybe need toml for that)

then sorting would be as easy as {{ range site.RegularPages.ByParam "min" }}

Sorting by calculated value is uncomfortable. Most of the time one would use a map, but there the keys are always strings, so printf "%04d" min is your friend for the sort order.

here’s a version using a newScratch to create a map that uses lists of pages with the same min. Each “key” holds a list of pages with the same min age taken from your ages param.

{{ $map := newScratch }}
{{ range $p := site.RegularPages }}
  {{ $min := printf "%04s" (index (split $p.Params.ages "-") 0) }}
  {{ with $map.Get $min }}
    {{ $map.Add $min $p }}
  {{ else }}
    {{ $map.Add $min (slice $p) }}
  {{ end }}
{{ end }}

to walk that map of arrays use that one

first range gets the list of pages sorted by min
second traverses the individual pages

{{ range sort $map.Values }} 
  {{ range . }}
    {{ $min := printf "%04s" (index (split .Params.ages "-") 0) }}
    {{ warnf "XP: %s -> %s -> %s " .Path $min .Params.ages }}
  {{ end }}
{{ end }}

That worked a treat, and you and @chrillek are certainly right. At the end of day adding the frontmatter key (and adjusting other templates) makes more sense.

I did manage to get your example working nicely, so thank you very much for that!

For future searchers, I ended up with:

  {{ $daymap := newScratch }}
  {{ range $i := (where $.Page.Pages "Params.duration" "day") }}
    {{ $min := index ($i.Params.ages | findRE "^\\d+") 0  }}
    {{ with $daymap.Get $min }}
      {{ $daymap.Add $min $i }}
    {{ else }}
      {{ $daymap.Add $min (slice $i) }}
    {{ end }}
  {{ end }}

  {{ range sort $daymap.Values }}
    {{ range . }}
      {{ .Params.ages }} ==> {{ .Title }}<br>
    {{ end }}
  {{ end }}

just found that one which which does it similar but less complicated.

depending on the number of pages the one or other may perform better.