How do I destructure a range in Ruby?

The beginning and end? I'd use:

foo = 1..2
foo.min # => 1
foo.max # => 2

Trying to use destructuring for a range is a bad idea. Imagine the sizes of the array that could be generated then thrown away, wasting CPU time and memory. It's actually a great way to DOS your own code if your range ends with Float::INFINITY.


end is not the same as max: in 1...10, end is 10, but max is 9

That's because start_val ... end_val is equivalent to start_val .. (end_val - 1):

start_value = 1
end_value = 2

foo = start_value...end_value
foo.end # => 2
foo.max # => 1

foo = start_value..(end_value - 1)
foo.end # => 1
foo.max # => 1

max reflects the reality of the values actually used by Ruby when iterating over the range or testing for inclusion in the range.

In my opinion, end should reflect the actual maximum value that will be considered inside the range, not the value used at the end of the definition of the range, but I doubt that'll change otherwise it'd affect existing code.

... is more confusing and leads to increased maintenance problems so its use is not recommended.


You can use minmax to destructure ranges:

min, max = (1..10).minmax
min # => 1
max # => 10

If you are using Ruby before 2.7, avoid using this on large ranges.


No, Until I am proven incorrect by Cary Swoveland, Weekly World News or another tabloid, I'll continue believing without any evidence that the answer is "no"; but it's easy enough to make.

module RangeWithBounds
  refine Range do
    def bounds
      [self.begin, self.end]
    end
  end
end

module Test
  using RangeWithBounds
  r = (1..10)
  b, e = *r.bounds
  puts "#{b}..#{e}"
end

Then again, I'd just write "#{r.begin.number_to_currency}..#{r.end.number_to_currency}" in the first place.

Tags:

Ruby