.Weight is broken

Sorry for the clickbait title, but this is really annoying me.
See, I have a project with dozens of pages in the same section, which are not going anywhere.
I wanted to order the page collection by filenames, which are numbered: this is not possible easily, not with basenames, that is.
But it won’t do with weights easier, because decimal weights apparently won’t work.

the section goes like that:
/home/drm/DACTYLO-HUGO/content/docs
├──􀇱 0-bis.md
├──􀇱 0.md
├──􀇱 1.md
├──􀇱 10.md
├──􀇱 11.md
├──􀇱 12.md
├──􀇱 13.md
├──􀇱 14.md
├──􀇱 15.md
├──􀇱 16.md
├──􀇱 17.md
├──􀇱 18.md
├──􀇱 19.md
├──􀇱 2-bis.md
├──􀇱 2.md
├──􀇱 20-2.md
├──􀇱 20.md

Given that it will eventually adds up to a hundred, and sometimes they are inserted in the middle, I need a scheme that doesn’t require editing ten files just to add new content. I’d be happy if I could add a weight such as 10.5 for 10-bis for instance.

0 or 1,5 is the same as no weight at all, 1.5 is the same as 1 but takes precedence over 1, and negative values take precedence over everything.
This doesn’t make sense, it should be -2<-1.5<-1<0<1<1.5<2 with no special value.

looks like you want the things to behave as you want them to behave. I would say there’s nothing broken but works as designed. (technical details section at the end)

back to the good old C64 times where we had to manually number lines in source code, you could start with 1000, 2000, 3000 - will last some time

but I would go with this one:

  • use a new param that is a float:
    [params]
    order = 23.2
    
  • use it like this
    site.RegularPages.ByParam "order"
    
  • you may use 0 or 0.0 here (see below)

or if there’s a rule implement some custom automatic sort partial(Cached) where you split the .Path by hyphen and sort by number and title.

gory details

The weight field in front matter is an int. the .Page.Weight method returns an int.

From the Go conversion spec:

When converting a floating-point number to an integer, the fraction is discarded (truncation towards zero).

so your sequence: -2 < -1.5 < -1 < 0 < 1 < 1.5 < 2 actually is: -2 -1 -1 0 1 1 2 and will be sorted but Unweighted or zero-weighted pages are placed at the end of the collection which results in -2 -1 -1 1 1 2 0.

Do not use 0 or 0.0 as .Weight

conclusion

Turning weight into a float would be a change request but as floats are much slower than int’s and lack proper equality comparision … guess someone will say no

I do share the performance concern, but since the program converts floats into integer anyway instead of throwing errors, I am unsure how doing comparisons instead of conversions would impact performance so much.

Can you give an exemple of changing the Path ?

just to be clear: We cannot change the path.

We can use the path for custom sorting.

Do you want to sort by the content file path or the logical path (publishing path)?

sort order should be:

  1. by the number in front of the hyphen
  2. by the text after the hyphen
  3. number only (no hyphen) should be first

right?

Order should be: 0.md > 0-bis.md > 1.md > 2.md > 2-bis.md
How I get there I don’t care, as long as (hopefully) I don’t need to write out manually wanky weights or a custom parameter.
I want to sort by the content file path in a given section, yes, not the URL or anything like that.

here’s a partial that takes a list of pages and sorts them as described:

just the bare logic - no error checking. for example

  • no check if the thing before the first hyphen is empty or not a number
  • no test if the page is backed by a file
  • stuff after the - starting with digits is sorted by ascii

call it like this:

{{- range (partial "sortPages" site.RegularPages) -}}
  {{- warnf "%s" .Path }}
{{- end }}

_partials/sortPages.html

{{- /* use map functionality of newScratch which is much faster than maps merge when using a dict */ -}}
{{- $sortedPages := newScratch }}
{{- range . }}
  {{- $sortkeys := split .File.BaseFileName "-" }}
  {{- /* all before first hyphen */ -}}
  {{- $key1 := index $sortkeys 0 }}
  {{- /* rejoin the remainer of the elements created by split */ -}}
  {{- $key2 := delimit ($sortkeys | last (sub (len $sortkeys) 1)) "-" }}
  {{- /*
    sort is always ascii, make sure 2 is greater than 10 by filling up to 10 digits
    use that one combined with the rest as map key and set the current page as "value"
    */ -}}
  {{ $sortedPages.SetInMap "toSort" (printf "%010s-%s" $key1 $key2) . }}
{{- end }}
{{- /* sorting a map by key removes the key */ -}}
{{ return $sortedPages.GetSortedMapValues "toSort" }}

the splitting could be done with regex - too late for today :wink:

ok couldn’t resist, here it is:

  • errors out if a filename does not start with a digit
  • does not need a hyphen after the first digits

can be easily adjusted by changing the regex

{{- $sortedPages := newScratch }}
{{ $key1 := ""}}
{{ $key2 := ""}}
{{- range $page := . }}
  {{ with findRESubmatch `^(\d+)(.*)$` .File.BaseFileName 1 }}
    {{- $key1 = index (index . 0) 1 }} {{/* first submatch: the starting digits */}}
    {{- $key2 = index (index . 0) 2 }} {{/* second submatch: the rest */}}
    {{- $sortedPages.SetInMap "toSort" (printf "%010s%s" $key1 $key2) $page }}
  {{ else }}
    {{- errorf "page '%s' : filename does not start with a digit: '%s'" .Path .File.BaseFileName }}
  {{ end }}
{{- end }}
{{ return $sortedPages.GetSortedMapValues "toSort" }}
1 Like

Just some believe system notes thrown in from the sidelines, but I do numbered filenames for the past 20 years in 001.txt, 002.txt, etc. format because that is sortable even for the worst sorter.

It’s not a Hugo issue that 10 comes before 2 if the sorting is alphabet/string based, that’s the same with any file sorting system. Some systems have an added “natural” sorting algorithm that understands when you are counting, but for instance. I also don’t suggest increments of 1, because that will lead to issues down the road when we decide to put something between page a and b. It’s static, but you want it extensible. Take a long enough zerofilled number as filename and then increment in 10s or 100s. Sort sections by increment*10 or increment*100 (docs are from 01000-title.md to 01999-title.md) so you end up with a nicely sortable scriptkiddie sortation.

Putting too much information into the filename (sorting, numbering of headlines, title) will lead to issues. That’s why you have the front matter. If the properties that are there by default don’t work, then add params frontmatter and work with your own system.

But I want the URL of the page to stay at /docs/1-title! — use slug front matter.
But I want to add a new page in front of all the other pages! — use negative weights.

Regarding the front matter sorting: Do integers. You will thank yourself later if you use any other system that parses front matter. Floats have issues in many programming languages. Even “proper” money calculating libraries make you save and calculate with 100 cent instead of 1 dollar if you want to round on cent. How this number looks like on the CMS side is task of the CMS.

For your specific issue I would “just” multiply the weight front matter by 100 or even by 1000. Then segment by .Section = 'docs' and sort by weight. This should take care of the sorting of the current content. If you have a new page between 01200-title.md and 01300-title.md then add it at 01250-title.md.

1 Like

valid points @davidsneighbour

<tr;tr> - just thome thoughts

yeah, indeed but Hugo makes it harder cause you cannot have numerical keys. And you cannot add info to the page. Although we could do a little more complex stuff and do a number sort.

working file based a file identifies the source. decoupling source and target may lead to issues later :wink: find your source for a page for example is not just a one click.

will work a period of time, depends which sections get new content how often … but I coded much years ago having these manual ascii sort an on the long run it hit me most of the time. It halves the available page range every time :wink:

for this use case, it’s just a compare of manual values, no calculation. I don’t see the floating calculation problems here but on the long run… with 1.25e-20 :rofl:

if the expected number of pages is not too high, and injecting a page in the middle is not a daily thing I also would go with these zero filled numbers in the file name.

2 Likes

I finally opted for zero-padded names. Less readable than the natural numbers (corresponding to the exercice number) but close enough and I don’t need to wonder how these stupidly dumb systems are gonna treat them.
Seriously, we’re in 2026 and most systems (filesystems, linux, windows, Go libraries, etc) still aren’t smart enough by default to offer natively and simply to sort files in various human-centric ways, using metadata and across directories ? Pathetic.
I’ll never have more than three entries between integers or more than 200 integer entries in total, 0105 to denote 10-bis is adequate.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.