views:

113

answers:

1

I am trying to override Ruby's <=> (spaceship) operator to sort apples and oranges so that apples come first sorted by weight, and oranges second, sorted by sweetness. Like so:

module Fruity
  attr_accessor :weight, :sweetness

  def <=>(other)
    # use Array#<=> to compare the attributes
    [self.weight, self.sweetness] <=> [other.weight, other.sweetness]
  end
  include Comparable
end

class Apple
include Fruity

def initialize(w)
  self.weight = w
end

end

class Orange
include Fruity

def initialize(s)
  self.sweetness = s
end

end

fruits = [Apple.new(2),Orange.new(4),Apple.new(6),Orange.new(9),Apple.new(1),Orange.new(22)]

p fruits

#should work?
p fruits.sort

But this does not work, can someone tell what I am doing wrong here, or a better way to do this?

+6  A: 

Your problem is you are only initializing one of the properties on either side, the other one will still be nil. nil isn't handled in the Array#<=> method, which ends up killing the sort.

There are a few ways to handle the problem first would be something like this

[self.weight.to_i, self.sweetness.to_i] <=> [other.weight.to_i, other.sweetness.to_i]

nil.to_i gives you 0, which will let this work.

Matt Briggs
thanks, it worked. They are ordered by weight then sweetness, but in descending order. Is there a 'best' way to order them ascending?
let me clarify my above comment-- if i make the following change: [self.weight.to_i, self.sweetness.to_i] <=> [other.weight.to_i, other.sweetness.to_i]I get oranges first ascending, then apples ascending, but I am not able to get apples first ascending, then oranges ascending. I have tried permuting the arrays, and even toying with Array#reverse with no luck.Also, does it matter if the Comparable mixin is included before or after the <=> definition? I am getting similar results either way.
it doesn't really matter where you put the include. maybe try result.sort.reverse ? not the best way, but probably the most straightforward
Matt Briggs
that gives me oranges first then apples, but i'm trying to get apples first sorted ascending, followed by oranges ascending. Still no luck. Anyone have any ideas?
okay, so why is it that Array#sort does not use its own <=> method to perform the comparisons, but instead uses my overridden one?? Doesn't this violate ruby's dynamic method lookup?
You are overriding <=> on fruity, not array.
Matt Briggs