Reputation: 145
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
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
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