3

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 ?

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I suspect that equality is well-defined math concept for a Range, but that there is not such a well-defined concept of "greater than" or "less than" - at least not any good enough to make it into the language core, you can always invent something that would work for you.
@NeilSlater agree, there is not a well-defined neither a common-sense way to tell from two different ranges which one is greater.
2

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]

2 Comments

nil is a perfectly valid return value for <=>, and it is used exactly like it's supposed to in this case. It means "these two objects cannot be sensibly ordered".
@Jörg W Mittag: Yes. I may need to adjust my first sentence. I don't mean "bad, period" I mean "bad for use in sorting"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.