Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions lib/liquid/standardfilters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -375,18 +375,19 @@ def join(input, glue = ' ')
# Sorts the items in an array in case-sensitive alphabetical, or numerical, order.
# @liquid_syntax array | sort
# @liquid_return [array[untyped]]
def sort(input, property = nil)
# @liquid_optional_param order: [string] Which direction to sort the array in. Default is "asc". Valid values are "asc" and "desc".
def sort(input, property = nil, options = {})
ary = InputIterator.new(input, context)

return [] if ary.empty?

if property.nil?
ary.sort do |a, b|
nil_safe_compare(a, b)
nil_safe_compare(a, b, options['order'])
end
elsif ary.all? { |el| el.respond_to?(:[]) }
begin
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
ary.sort { |a, b| nil_safe_compare(a[property], b[property], options['order']) }
rescue TypeError
raise_property_error(property)
end
Expand All @@ -404,18 +405,19 @@ def sort(input, property = nil)
# > string, so sorting on numerical values can lead to unexpected results.
# @liquid_syntax array | sort_natural
# @liquid_return [array[untyped]]
def sort_natural(input, property = nil)
# @liquid_optional_param order: [string] Which direction to sort the array in. Default is "asc". Valid values are "asc" and "desc".
def sort_natural(input, property = nil, options = {})
ary = InputIterator.new(input, context)

return [] if ary.empty?

if property.nil?
ary.sort do |a, b|
nil_safe_casecmp(a, b)
nil_safe_casecmp(a, b, options['order'])
end
elsif ary.all? { |el| el.respond_to?(:[]) }
begin
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property], options['order']) }
rescue TypeError
raise_property_error(property)
end
Expand Down Expand Up @@ -1005,11 +1007,17 @@ def apply_operation(input, operand, operation)
result.is_a?(BigDecimal) ? result.to_f : result
end

def nil_safe_compare(a, b)
def nil_safe_compare(a, b, order)
order = order ? Utils.to_s(order).downcase : 'asc'

if order != 'asc' && order != 'desc'
raise Liquid::ArgumentError, "invalid order provided to sort_natural. '#{order}' was provided"
end

result = a <=> b

if result
result
result * (order == 'desc' ? -1 : 1)
elsif a.nil?
1
elsif b.nil?
Expand All @@ -1019,9 +1027,15 @@ def nil_safe_compare(a, b)
end
end

def nil_safe_casecmp(a, b)
def nil_safe_casecmp(a, b, order)
order = order ? Utils.to_s(order).downcase : 'asc'

if order != 'asc' && order != 'desc'
raise Liquid::ArgumentError, "invalid order provided to sort_natural. '#{order}' was provided"
end

if !a.nil? && !b.nil?
a.to_s.casecmp(b.to_s)
a.to_s.casecmp(b.to_s) * (order == 'desc' ? -1 : 1)
elsif a.nil? && b.nil?
0
else
Expand Down
18 changes: 18 additions & 0 deletions test/integration/standard_filter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,15 @@ def test_sort_with_nils
assert_equal([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }, {}], @filters.sort([{ "a" => 4 }, { "a" => 3 }, {}, { "a" => 1 }, { "a" => 2 }], "a"))
end

def test_sort_with_order
assert_equal([4, 3, 2, 1], @filters.sort([4, 3, 2, 1], nil, "order" => "desc"))
assert_equal([{ "a" => 4 }, { "a" => 3 }, { "a" => 2 }, { "a" => 1 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 2 }, { "a" => 1 }], "a", "order" => "desc"))

assert_raises(Liquid::ArgumentError) do
@filters.sort([1, 2], nil, "order" => "invalid")
end
end

def test_sort_when_property_is_sometimes_missing_puts_nils_last
input = [
{ "price" => 4, "handle" => "alpha" },
Expand Down Expand Up @@ -341,6 +350,15 @@ def test_sort_natural_with_nils
assert_equal([{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }, {}], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, {}, { "a" => "a" }, { "a" => "B" }], "a"))
end

def test_sort_natural_with_order
assert_equal(["D", "c", "B", "a"], @filters.sort_natural(["c", "D", "a", "B"], nil, "order" => "desc"))
assert_equal([{ "a" => "D" }, { "a" => "c" }, { "a" => "B" }, { "a" => "a" }], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, { "a" => "a" }, { "a" => "B" }], "a", "order" => "desc"))

assert_raises(Liquid::ArgumentError) do
@filters.sort_natural(["a", "b"], nil, "order" => "invalid")
end
end

def test_sort_natural_when_property_is_sometimes_missing_puts_nils_last
input = [
{ "price" => "4", "handle" => "alpha" },
Expand Down
Loading