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:
Is there a more concise way of doing this? I know this code isn't the ugliest, but something in my gut tells me there's got to be a better way. Especially if we have more than 4 variables.
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
···
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:
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
Those solutions are definitely cool but I find James' to be more readable so that's what I'll use.
Robert Klemme wrote:
···
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