Project

General

Profile

Actions

Feature #20080

closed

Introduce #bounds method on Range

Added by stuyam (Stuart Yamartino) 5 months ago. Updated 2 months ago.

Status:
Feedback
Assignee:
-
Target version:
-
[ruby-core:115864]

Description

Followup Reference: #20027

Update 1/11/24: (based on many wonderful suggestions!)

  1. Call the method #bounds.
first, last = (1..300).bounds # => [1, 300]
first, last = (300..1).bounds # => [300, 1]
first, last = (..300).bounds # => [nil, 300]
first, last = (1..).bounds # => [1, nil]
  1. Add exclude_end? support so re-hydration of Range works:
b = (1..2).bounds  #=> [1,2]
Range.new(*b)      #=> 1..2

b = (1...2).bounds #=> [1,2,true]
Range.new(*b)      #=> 1...2

I did a better job of outlining use cases in this comment below so I will let that speak for itself: https://bugs.ruby-lang.org/issues/20080#note-3

Update: 2/13/24
Browsing the ruby codebase I noticed that the #as_json method on Range (when you require 'json/add/range') does something similar to what the #bounds method we are describing is doing: https://github.com/ruby/ruby/blob/master/ext/json/lib/json/add/range.rb#L34

{
  JSON.create_id  => self.class.name,
  'a'             => [ first, last, exclude_end? ]
}

This tells me we are on the right track. Though the difference here is that exclude_end? is always included. I am thinking the #bounds should maybe always be including the exclude_end? piece rather than only include it if it's true. I am inclined to suggest always including the exclude_end? regardless of if it is true or false to avoid confusion or people ever calling #last on the #bounds results thinking it is the #last value where it could be the #last or #exclude_end? value depending on the type or range. This would still satisfy all of the use cases outlined in this comment: https://bugs.ruby-lang.org/issues/20080#note-3

Therefore I think #bounds should just always return: [ first, last, exclude_end? ]

Original Proposal:
This feature request is to implement a method called #begin_and_end on Range that returns an array of the first and last value stored in a range:

(1..300).begin_and_end #=> [1, 300]

first, last = (300..1).begin_and_end
first #=> 300
last #=> 1

I believe this would be a great addition to Ranges as they are often used to pass around a single object used to hold endpoints, and this allows easier retrieval of those endpoints.
This would allow easier deconstruction into start and end values using array deconstruction as well as a simpler way to serialize to a more primitive object such as an array for database storage.
This implementation was suggested by @mame (Yusuke Endoh) in my initial feature suggestion regarding range deconstruction: https://bugs.ruby-lang.org/issues/20027

This implementation would work similar to how #minmax works where it returns an array of two numbers, however the difference is that #minmax doesn't work with reverse ranges as @Dan0042 (Daniel DeLorme) pointed out in the link above:

(1..42).minmax #=> [1, 42]
(42..1).minmax #=> [nil, nil]
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like1Like0Like0Like0Like0Like0Like0Like0Like1Like0