Jekyll Liquid中sort与sort_natural行为差异原因咨询
问题
作为资深开发者,我对Ruby和Liquid经验较少。在Jekyll中尝试对分类进行不区分大小写排序时发现:
- 使用
sort过滤器处理site.categories哈希时,会将其转换为键值对数组并排序,输出示例为Apple / Aviation / Vapor / macOS / visionOS(但区分大小写); - 而
sort_natural过滤器却将整个哈希包装为单元素数组,无法实现分类排序。
二者Ruby实现仅比较函数不同,咨询行为差异的原因。
相关代码及结构示例:
Liquid模板代码:
{% assign categories = site.categories | sort %} {% for cat in categories %} {{ cat | first }} / {% endfor %}
site.categories结构:
{"Misc"=>[#<Jekyll::Document _posts/2015-02-17-burningman-solar-power.html collection=posts>, …], …}
sort处理后结构:
[["Apple", [#<Jekyll::Document _posts/2024-02-09-AVP-Blender-Workflow.md collection=posts>, …]], …]
sort_natural处理后结构:
[{"Misc"=>[#<Jekyll::Document _posts/2015-02-17-burningman-solar-power.html collection=posts>, …], …}]
二者Ruby实现:
# 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
原因分析
核心差异在于两个过滤器使用的比较方法对哈希转换后的元素支持不同:
sort过滤器的逻辑:- 处理哈希时,
InputIterator会自动将哈希转换为键值对数组(每个元素是[分类名, 文章数组]的数组)。 - 比较方法
nil_safe_compare调用Ruby的<=>运算符,而Array#<=>支持数组元素的顺序对比,因此可以正常对键值对数组排序,最终返回排序后的分类键值对数组。
- 处理哈希时,
sort_natural过滤器的逻辑:- 同样会通过
InputIterator将哈希转换为键值对数组,但比较方法nil_safe_casecmp依赖于字符串的casecmp方法(用于不区分大小写比较)。 - 键值对数组的元素是数组而非字符串,调用数组的
casecmp方法会抛出TypeError,而sort_natural中property为nil的分支没有错误捕获逻辑,导致排序操作失败。 - 最终Liquid会回退到将原哈希包装为单元素数组返回,而非预期的排序后键值对数组。
- 同样会通过
解决方法
要实现不区分大小写的分类排序,需先将哈希显式转换为键值对数组,再指定按分类名(数组的第一个元素)进行自然排序:
{% assign categories = site.categories | to_a | sort_natural: 0 %} {% for cat in categories %} {{ cat | first }} / {% endfor %}
to_a:将site.categories哈希转换为键值对数组;sort_natural: 0:指定按数组的第0个元素(即分类名)进行不区分大小写的自然排序。
内容的提问来源于stack exchange,提问作者Rick
相关产品推荐
相关产品推荐

