praveenag
praveenag

Reputation: 145

sort an array of ranges

How to sort an array of ranges

ranges = [Range.new(0, 3, true), Range.new(3, 5, true), Range.new(5, 7, true), Range.new(7, 9, true), Range.new(9, 11, true), Range.new(11, 100, true)]
ranges.sort
=> ArgumentError: comparison of Range with Range failed
from (irb):7:in `sort'
from (irb):7
from /Users/praveena/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'

But when I try

2.0.0p247 :022 > (3...4) <=> (4...8)
=> nil
2.0.0p247 :023 > (3...4) <=> (1...2)
=> nil

Am i missing something ?

Upvotes: 3

Views: 1270

Answers (2)

Neil Slater
Neil Slater

Reputation: 27207

nil is a not a usable value from a comparison operation for sorting.

If you try <=> between two Comparable objects, a <=> b they will always return -1, 0 or 1 for "a less than b", "a equals b", and "a greater than b" respectively.

Therefore, to sort Range objects, you will need to override <=> and define yourself what order they should sort in. Note this will have to be something you have made up in order to sort them, Range objects do not have an inherent or meaningful sort order.

For example, I could decide that ranges are sorted in order of start of a Range, and falling back to end of the Range if they are equal:

class Range
  def <=>(other)
    [min, max] <=> [other.min, other.max]
  end
end

[(1..3),(1...3),(4..5),(2..3)].sort
 => [1...3, 1..3, 2..3, 4..5]

Upvotes: 2

fotanus
fotanus

Reputation: 20116

It seems that range have an implementation for <=>, but is not complete. Lets check:

> Range.new(3,4) <=> Range.new(3,4)
=> 0
# It seems that it correctly identify when the two are equals

> Range.new(3,4) <=> Range.new(4,4)
=> nil
# But it seems that it fails to fail when both are different!

This method is defined in Range because it is actually defined on the Object class(!!!), thus every object has this method defined, which doesn't means it works. Actually the implementation for range is the default one. Lets check this:

# lets define a dummy class
class A
end
=> nil

# and create an object
a = A.new
=> #<A:0x9b1d998>

# can correctly identify when equal
a <=> a
=> 0

# but invalid answer when not equal!
a <=> 1
=> nil

At this point, you should now understand what is happening in your code.

It is totally understandable that range does not have a canonical <=> method because there is no mathematical definition for a greater range (that I know), neither a common sense definition.

Upvotes: 2

Related Questions