James Edward Gray II wrote:
> On Aug 17, 2005, at 10:16 PM, Lowell Kirsh wrote:
> 
>> I have a class called foo and I want to make it comparable so that
>> I can sort arrays of Foo. Here is the class:
>> 
>> class Foo
>>     attr_accessor :a, :b, :c, :d
>>     def <=>(rhs); (see below); end
>> end
>> 
>> I'm assuming that the a,b,c, and d field are each Comparable. To
>> compare 2 Foos, we first look at the a's and if they're not the
>> same, we return the result of their comparison. If they are the
>> same, we continue to compare the b's, and if they're the same we
>> compare the c's, and so on until we compare the last field. So for
>> my definition of <=> I have the following, which works, but I'd
>> like something better/shorter:
> 
> def <=>( other )
>    [@a, @b, @c, @d] <=> [other.a, other.b, other.c, other.d]
> end
> 
> Arrays are Comparable and they compare each of their contents in
> order. 
> 
> Hope that helps.
> 
> James Edward Gray II

Here are some other possible implementations using #inject... :-)

require 'enumerator'

Foo = Struct.new :a,:b,:c,:d

class Foo
  include Comparable
  
  def <=>(o)
    members.inject(0) do |cmp,field|
      return cmp unless cmp == 0
      send(field) <=> o.send(field)
    end
  end
  
  # alternative
  def <=>(o)
    cmp = 0
    each_pair do |field, val|
      cmp = val <=> o.send(field)
      return cmp unless cmp == 0
    end
    cmp
  end
  
  # alternative
  def <=>(o)
    to_enum(:each_pair).inject(0) do |cmp,(field, val)|
      cmp = val <=> o.send(field)
      return cmp unless cmp == 0
    end
  end

end

>> f1 = Foo.new 1,2,3,4
=> #<struct Foo a=1, b=2, c=3, d=4>
>> f2 = Foo.new 1,2,3,3
=> #<struct Foo a=1, b=2, c=3, d=3>
>> puts f1 <=> f2
1
=> nil


Kind regards

    robert