I’ve been struggling with sorting categories in Jekyll case-insensitively. Writing the following Liquid template works well, except it’s not sorted case-insensitively:
As near as I can tell, sort and sort_natural have identical implementations, except for the compare (listings below).
{% assign categories = site.categories | sort %}
{% for cat in categories %}
{{ cat | first }} /
{% endfor %}
This gives output like this (notice how the last two start with a lower-case letter and aren’t naturally sorted):
Apple / Aviation / Vapor / macOS / visionOS
site.categories is a hash with the name of the category as the key, and an array of document objects as the value:
{"Misc"=>[#<Jekyll::Document _posts/2015-02-17-burningman-solar-power.html collection=posts>, …], …}
Passing that to sort gives an array of arrays, where the inner array has two items: the name of the category, and the array of documents. This is the structure I want:
{{ site.categories | sort | inspect }}
[["Apple", [#<Jekyll::Document _posts/2024-02-09-AVP-Blender-Workflow.md collection=posts>, …]], …]
sort_natural doesn’t process the hash the same way:
{{ site.categories | sort_natural | inspect }}
[{"Misc"=>[#<Jekyll::Document _posts/2015-02-17-burningman-solar-power.html collection=posts>, …], …}]
Basically packaging up the hash into an array of one element.
sort and sort_natural are implemented like this. As far as I can see, they are identical except for the comparison function.
# Sort elements of the array
# provide optional property with which to sort an array of hashes or drops
def sort(input, property = nil)
ary = InputIterator.new(input)
return [] if ary.empty?
if property.nil?
ary.sort do |a, b|
nil_safe_compare(a, b)
end
elsif ary.all? { |el| el.respond_to?(:[]) }
begin
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
rescue TypeError
raise_property_error(property)
end
end
end
# Sort elements of an array ignoring case if strings
# provide optional property with which to sort an array of hashes or drops
def sort_natural(input, property = nil)
ary = InputIterator.new(input)
return [] if ary.empty?
if property.nil?
ary.sort do |a, b|
nil_safe_casecmp(a, b)
end
elsif ary.all? { |el| el.respond_to?(:[]) }
begin
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
rescue TypeError
raise_property_error(property)
end
end
end
I’m a fairly expert developer, but have very little experience with Ruby (or Liquid).
Why do these two Liquid template tags behave differently?