I'm displaying Authors on a page (Rails view) like so:
Author.where(featured: true).where.not(username: nil).order(:priority, :name)
However I have recently added a new model Photographer
and I would like to combine the photographers with the authors and sort them similarly. They share most of the same columns.
I've tried:
combined = (Author.where(featured: true).where.not(username: nil) Photographer.where(featured: true).where.not(username: nil))
combined.order(:priority, name)
=> NoMethodError (undefined method `order' for #<Array:0x00005573bc2ab3b0>)
combined.sort_by {|item| [item.priority, item.name]}
=> ArgumentError (comparison of Array with Array failed)
One caveat is that in some cases priority
would be nil
. So in that event it should order by name instead.
Any help is appreciated!
CodePudding user response:
The issue is that nil
is only comparable with nil
:
nil <=> nil # => 0 # -1, 0, 1 are good
"priority" <=> nil # => nil # `nil` means not comparable
# and you get an error from `sort_by`
Solution is to avoid comparing nil
with anything else or explicitly handle the comparison.
You can use sort
and sort the nil
s yourself:
ary = (Author.all Photographer.all)
ary.sort do |a, b|
if a.priority && b.priority # priority <=> priority
a.priority <=> b.priority
elsif a.priority # priority <=> nil
-1 # nil is lower
elsif b.priority # nil <=> priority
1 # priority is higher
else # nil <=> nil
a.name <=> b.name # break the tie
end
end
You can split array in two, with priority and without priority, and sort each one. That way priority
is compared to priority
; nil
is compared to nil
, which is 0, and it falls back to comparing name
(like the sort
above, but elsif's never match):
ary.partition(&:priority).flat_map do |part|
part.sort_by { |i| [i.priority, i.name] }
end
Or a dumber way of doing this (it's also faster) is to make nil
be something last:
ary.sort_by { |i| [i.priority||"zzz", i.name] }