From: Robert Klemme Date: 2006-02-05T02:18:28+09:00 Subject: Re: how to vary sort's block? "Wybo Dekker" schrieb im Newsbeitrag news:Pine.LNX.4.61.0602041523170.10213@servalys.nl... > On Sat, 4 Feb 2006, Robert Klemme wrote: > >> The easiest is probably to use sort_by. >> >> Thing = Struct.new(:name, :amount) >> arr = [ >> Thing.new('John',10), >> Thing.new('Anny',20), >> ] >> >> p arr.sort_by {|x| x.name} >> p arr.sort_by {|x| x.amount} > > this does not allow for more complex sorts, like on name *and* amount, > does it? It does: arr.sort_by {|x| [x.name, x.amount]} (depending on the precedence you want). I believe, if you use Struct and want to order according to field order then you might be able to just use sort() without arguments (alternatively sort_by {|x| x.to_a} - to_a is defined by Struct). > I saw that it is also possible to give sort an argument instead of a > block: > > require 'pp' > > Thing = Struct.new(:name, :amount) > arr = [ > Thing.new('John',80), > Thing.new('John',10), > Thing.new('John',30), > Thing.new('Anny',20), > Thing.new('Anny',10), > Thing.new('Anny',30), > Thing.new('Anny',15) > ] > > def byname(a,b) a.name <=> b.name end > def byamount(a,b) a.amount <=> b.amount end > def by_name_amount(a,b) > (a.name <=> b.name)*4 + (a.amount <=> b.amount) > end > > [:byname,:byamount,:by_name_amount].each do |order| > puts "sort #{order}:" > pp arr.sort(&method(order)) > end > > But ri does not tell me that: > > -------------------------------------------------------- Enumerable#sort > enum.sort => array > enum.sort {| a, b | block } => array > ------------------------------------------------------------------------ > Returns an array containing the items in _enum_ sorted, either > according to their own +<=>+ method, or by using the results of the > supplied block. The block should return -1, 0, or +1 depending on > the comparison between _a_ and _b_. As of Ruby 1.8, the method > +Enumerable#sort_by+ implements a built-in Schwartzian Transform, > useful when key computation or comparison is expensive.. > > %w(rhea kea flea).sort #=> ["flea", "kea", "rhea"] > (1..10).sort {|a,b| b <=> a} #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] Actually ri tells you that - because it's the block form. Your methods are implicitely converted to blocks. In that case I'd rather not use a method definition but rather do the more direct lambda / proc: Thing::BYNAME = lambda {|a,b| a.name <=> b.name} Thing::BYAMOUNT = lambda {|a,b| a.amount <=> b.amount} .... arr.sort(&Thing::BYNAME) You can as well define those constants in global scope but I felt they are more appropriately placed in Thing's scope because they refer exactly to Things. Kind regards robert