I have an Array
of Array
s that I want to sort by longest length to shortest. I achieved this easily enough with a sort_by
> a = [ [1, 2, 9],
[4, 5, 6, 7],
[1, 2, 3] ]
> a.sort_by(&:length).reverse # or a.sort_by {|e| e.length}.reverse
=> [[4, 5, 6, 7], [1, 2, 3], [1, 2, 9]]
What I want, however is to have a sort of tie-breaker for lists of equal length. If two lists' lengths are equal, the list whose last entry is greater should come first. So in the above, [1, 2, 9]
and [1, 2, 3]
should be switched.
I don't care abouth the case where two lists have both equal length and equal last element, they can be in whatever order if that occurs. I don't know if/how I can acheive this with ruby built-in sorting.
You can still do this with sort_by
, you just need to realize that Ruby arrays compare element-by-element:
ary <=> other_ary → -1, 0, +1 or nil
[...]
Each object in each array is compared (using the <=> operator).
Arrays are compared in an “element-wise” manner; the first two elements that are not equal will determine the return value for the whole comparison.
That means that you can use arrays as the sort_by
key, then throw in a bit of integer negation to reverse the sort order and you get:
a.sort_by { |e| [-e.length, -e.last] }
That will give you the [[4, 5, 6, 7], [1, 2, 9], [1, 2, 3]]
that you're looking for.
If you're not using numbers so the "negation to reverse the order" trick won't work, then use Shaunak's sort
approach.
There you go :
a = [ [1, 2, 9],[4, 5, 6, 7],[1, 2, 3] ]
a.sort { |a, b| (b.count <=> a.count) == 0 ? (b.last <=> a.last): (b.count <=> a.count) }
That should give you:
[[4, 5, 6, 7], [1, 2, 9], [1, 2, 3]]
How this works: we pass a block to sort function, which first checks if the array length is same, if not it continues to check for last element.
You could use
a.sort_by {|i| [i.length, i.last] }.reverse
# => [[4, 5, 6, 7], [1, 2, 9], [1, 2, 3]]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With