From: Steve Howell Date: 2010-10-15T14:40:24+09:00 Subject: Re: sort_by: multiple fields with reverse sort On Oct 14, 1:25 pm, Ryan Davis wrote: > On Oct 14, 2010, at 08:35 , Steve Howell wrote: > > >  module Enumerable > >    def sort_by_multi(*key_methods) > >      def compare(a,b, key_methods) > > This doesn't do what you think (or at least imply that) it does. Ok, I found and fixed a bug (*) and reworked the example to (hopefully) make the implied intent more clear. rows = [ {:name => "al", :salary => 40}, {:name => 'charlie', :salary => 100}, {:name => "al", :salary => 50}, {:name => 'diane', :salary => 1000, :commission => 40}, {:name => 'diane', :salary => 1000, :commission => 30}, {:name => 'ed', :salary => 20}, ] def do_sql_like_sort(rows) def asc(field) [Proc.new { |row| row[field] }, :asc ] end def desc(field) [Proc.new { |row| row[field] }, :desc ] end # The semantics for sort_by_multi are similar to # SQL. rows.sort_by_multi( asc(:name), desc(:salary), desc(:commission) ) end expected_result = [ {:name=>"al", :salary=>50}, {:name=>"al", :salary=>40}, {:name=>"charlie", :salary=>100}, {:name=>"diane", :salary=>1000, :commission => 40}, {:name=>"diane", :salary=>1000, :commission => 30}, {:name=>"ed", :salary=>20}, ] module Enumerable def sort_by_multi(*key_methods) # allow for multiple key_methods and only # evaluate them when they are truly needed # for the sort def compare(a,b, key_methods) i = 0 while i < key_methods.size do for elem in [a, b] do key = elem[1] if elem[0].size <= i elem[0] << key_methods[i][0].call(key) end end x, y = (key_methods[i][1] == :desc) ? [b, a] : [a, b] result = (x[0][i] <=> y[0][i]) return result unless result == 0 i += 1 end return result end self.collect do |item| [ [], item ] end.sort do |a, b| compare(a, b, key_methods) end.collect do |kv| kv[1] end end end sorted_rows = do_sql_like_sort(rows) if sorted_rows != expected_result raise 'fail' end * - The previous iteration of this code was swapping a and b instead of making copies before swapping. This broke the case where two of the fields were to be sorted in descending order.