-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add sort_numeric filter #1028
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add sort_numeric filter #1028
Conversation
| ary.sort do |a, b| | ||
| Utils.to_number(a) <=> Utils.to_number(b) | ||
| end | ||
| elsif ary.empty? # The next two cases assume a non-empty array. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this unnecessary (because ary.first.respond_to?(:[]) will be false if ary is empty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but wouldn't the else branch return nil instead of [] as this branch does?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep I'm an idiot
fw42
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 maybe add a test for the case where the property doesn't exist?
| ary.sort do |a, b| | ||
| Utils.to_number(a[property]) <=> Utils.to_number(b[property]) | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If none of these if/elsif branches are taken, then it seems like we should treat this as an error. Are you just returning nil in that case to keep consistency with the sort filter? Is consistency with the sort filter more important that providing a clearer error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we change sort as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could result in broken liquid being more obviously broken (i.e. showing Liquid Error: ...). Perhaps we should add logging for in the sort filter so that we will know whether it would break things, then we could turn it into a liquid error if it won't.
|
Sorry for just popping in but why not introducing a more generic |
|
Accepting a block is not yet a concept in Liquid. I'm not going to merge this yet though, because it's possible that could change. |
|
What happened to Travis CI? It doesn't seem to be running anymore |
|
For some reason Travis CI was disabled for this repo. I enabled it again, new commits should be built. |
|
@pushrax I've a couple of questions regarding the last branch of the conditional block in the current state: elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
ary.sort do |a, b|
Utils.to_number(a[property]) <=> Utils.to_number(b[property])
end
end
Refactoring to.. def sort_numeric(input, property = nil)
ary = InputIterator.new(input)
if property.nil?
ary.sort { |a, b| Utils.to_number(a) <=> Utils.to_number(b) }
elsif ary.empty?
[]
else
ary.sort do |a, b|
next unless a.respond_to?(:[]) && b.respond_to?(:[])
Utils.to_number(a[property]) <=> Utils.to_number(b[property])
end
end
end |
|
Checking just the first element for The nil check is also reasonably questionable, also copied from |
@pushrax I wrote a benchmark script to test this..: # frozen_string_literal: true
require 'benchmark/ips'
require 'liquid'
def sort_numeric_original(input, property = nil)
ary = Liquid::StandardFilters::InputIterator.new(input)
if property.nil?
ary.sort do |a, b|
Liquid::Utils.to_number(a) <=> Liquid::Utils.to_number(b)
end
elsif ary.empty?
[]
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
ary.sort do |a, b|
Liquid::Utils.to_number(a[property]) <=> Liquid::Utils.to_number(b[property])
end
end
end
def sort_numeric_proposed(input, property = nil)
ary = Liquid::StandardFilters::InputIterator.new(input)
if property.nil?
ary.sort { |a, b| Liquid::Utils.to_number(a) <=> Liquid::Utils.to_number(b) }
elsif ary.empty?
[]
else
ary.sort do |a, b|
next unless a.respond_to?(:[]) && b.respond_to?(:[])
Liquid::Utils.to_number(a[property]) <=> Liquid::Utils.to_number(b[property])
end
end
end
INPUT = [{ "a" => '10' }, { "a" => '3' }, { "a" => '1' }, { "a" => '2' }]
return unless sort_numeric_original(INPUT) == sort_numeric_proposed(INPUT)
return unless sort_numeric_original(INPUT, "a") == sort_numeric_proposed(INPUT, "a")
Benchmark.ips do |x|
x.report('original sort_numeric no-property') { sort_numeric_original(INPUT) }
x.report('proposed sort_numeric no-property') { sort_numeric_proposed(INPUT) }
x.compare!
end
Benchmark.ips do |x|
x.report('original sort_numeric') { sort_numeric_original(INPUT, "a") }
x.report('proposed sort_numeric') { sort_numeric_proposed(INPUT, "a") }
x.compare!
endOn my system, the numbers are: |
|
The constant overhead is dominating in that benchmark with just 4 elements. With an order of magnitude or two more elements the result should be different. Removing In any case, as mentioned your points were fair and the perf loss of checking all the elements for I'm was going to abandon this PR since I've been thinking about a more general change to let Liquid developers pass something like a block to filters, but that idea isn't fleshed out enough to happen soon and this filter is indeed useful and generic enough to be included. I'll probably circle back to it this week some time, thanks for the ping. |
|
Hey @pushrax, was browsing around for resources when I found the GH Issue and then this PR. Totally understand if you're busy and aren't planning on workin on this PR, but do you know of any workarounds or alternative implementations? Use case is that we're sorting a bunch of Docker image tags using Jekyll + Liquid, and we're pulling those values from a JSON file. So essentially we're sorting through a list of values that looks like this: |
|
Hi @pushrax! Are we going to see this in Liquid soon? It will be almost a year since this PR was opened, and people truly need this feature. Thank you, and all the best! |
|
This could likely be optimized after the fact, but for now I think this should just make it through, it seems to be some valid use cases held off by this. |
|
What about this PR? |
|
bump? :3 |
Closes #980