Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

Probably late, nevertheless...

add the following monkeypatch

class Array
  def to_i(default=Float::INFINITY)
    self.map do |element|
      element.nil? ? default : element.to_i
    end
  end
end

And change the body of Fruity::<=> to

[self.weight, self.sweetness].to_i <=> [other.weight, other.sweetness].to_i
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top