Reverse Sort
Published 2012-01-06 @ 15:42
Tagged ruby, thoughts, toys
It’s fairly common knowledge that you can sort and reverse a collection in one pass by using sort_by with a negated number:
1 2 3 4 |
tally = [["a", 2], ["b", 1], ["c", 2]] p tally.sort_by { |(str, num)| [-num, str] } # => [["a", 2], ["c", 2], ["b", 1]] |
but what happens if you need to reverse something like strings? Strings don’t respond to unary minus, so you can’t do this:
1 2 |
tally.sort_by { |(str, num)| [-str, num] } rescue nil # => undefined method `-@' for "a":String (NoMethodError) |
What you want to do is make this behavior dynamic and available everywhere:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
module Reversed def <=> target -super end end class Object def -@ self.dup.extend Reversed end end p tally.sort_by { |(str, num)| [-str, num] } # => [["c", 2], ["b", 1], ["a", 2]] |
What would be really special is if Ruby allowed you to apply modules multiple times so that -(-a) == a.