Custom sort algorithm with .ByParam?

I’m using Zettelkasten formatted IDs in the front matter of some pages on my site. The ID format is intentionally quite compact since I must often write them by hand, but the format still allows the ID to inform about hierarchy and which pages are related.

Each such page has a param in the front matter labeled tdgid. Sample pages have tdgid values like:


I’m currently ranging over all such pages successfully using:

range (where .Site.Pages ".Params.tdgid" "!=" nil).ByParam "tdgid"

This is almost what I want, but it’s lexically sorting RT6a10 and RT6a11 before RT6a2.

The simplest “solution” would be to always use two places for each field within the string, but since I handwrite these often I’d like to keep them as short as possible. I suppose “live with the broken ordering” is an even simpler solution. :wink:

My Go and Hugo skills are anemic at best. I’m hoping some kind soul with more experience than me could offer a suggestion and save me from a few hours of hacking.

Many thanks in advance.

There is currently no provision in Hugo for a “natural” sort. In my experience natural sort mechanisms are uncommon outside of file managers, so I generally plan accordingly (i.e., zero padding to n places).

1 Like

Yeah, I’m thinking along the same lines, but I’d rather not store the padded values in the front matter. I’d rather that humans never see the padded versions (to prevent confusion).

I’ll look into building up a map of padded IDs and paths to range over, and then use GetPage with each.

Padding isn’t as simple as prefixing the entire string with a padding character, though. I’ll need to pad each field within the string (each time it switches from alphabetic to numeric). I know I’ll never have more than, say, five levels of hierarchy, and will never need more than three characters in each field. I’m currently investigating replaceRE to add the padding.

Well, thanks to the tip from jmooring to focus on padding, I managed to solve my own problem.

The code seems ugly as sin to me, though. Reminds me of the “line noise” from some of my old perl scripts. Any pointers are extremely welcome.

My shortcode to list all the pages with tdgid now looks like this:

{{/* Build a map of paddedID -> Page */}}
{{- range (where .Site.Pages ".Params.tdgid" "!=" nil).ByParam "tdgid" -}}
  {{- $orig := .Params.tdgid -}}
  {{/* RT6a11b9 -> "6,a,11,b,9" */}}
  {{- $csv := replaceRE `^RT(\d+)(\D*)(\d*)(\D*)(\d*)` "$1,$2,$3,$4,$5" $orig -}}
  {{/* now split into an array: [6 a 11 b 9] */}}
  {{- $split := split $csv "," -}}
  {{- $s := slice "" }}
  {{/* now build a string up from padded fields -> "00600a01100b009" */}}
  {{- range $split -}}
    {{- $s = $s | append (printf "%03v" .) -}}
  {{- end -}}
  {{- $paddedID := delimit $s "" -}}
  {{- $.Scratch.SetInMap "paddedPages" (string $paddedID) .  -}}
{{- end -}}

{{/* HTML foo */}}

{{- range .Scratch.GetSortedMapValues "paddedPages" -}}

  {{/* page is in "." */}}

  {{/* more HTML here referring to .Params.tdgid, etc. */}}

{{- end -}}

I need a drink.


You deserve one! Well done.