class Range

A Range object represents a collection of values that are between given begin and end values.

You can create an Range object explicitly with:

Beginless Ranges

Abeginlessrange has a definite end value, but anil begin value. Such a range includes all values up to the end value.

r = (..4)# => nil..4r.begin# => nilr.include?(-50)# => truer.include?(4)# => truer = (...4)# => nil...4r.include?(4)# => falseRange.new(nil,4)# => nil..4Range.new(nil,4,true)# => nil...4

A beginless range may be used to slice an array:

a = [1,2,3,4]# Include the third array element in the slicer = (..2)# => nil..2a[r]# => [1, 2, 3]# Exclude the third array element from the slicer = (...2)# => nil...2a[r]# => [1, 2]

Methodeach for a beginless range raises an exception.

Endless Ranges

Anendlessrange has a definite begin value, but anil end value. Such a range includes all values from the begin value.

r = (1..)# => 1..r.end# => nilr.include?(50)# => trueRange.new(1,nil)# => 1..

The literal for an endless range may be written with either two dots or three. The range has the same elements, either way. But note that the two are not equal:

r0 = (1..)# => 1..r1 = (1...)# => 1...r0.begin==r1.begin# => truer0.end==r1.end# => truer0==r1# => false

An endless range may be used to slice an array:

a = [1,2,3,4]r = (2..)# => 2..a[r]# => [3, 4]

Methodeach for an endless range calls the given block indefinitely:

a = []r = (1..)r.eachdo|i|a.push(i)ifi.even?breakifi>10enda# => [2, 4, 6, 8, 10]

A range can be both beginless and endless. For literal beginless, endless ranges, at least the beginning or end of the range must be given as an explicit nil value. It is recommended to use an explicit nil beginning and end, since that is what Ruby uses forRange#inspect:

(nil..)# => (nil..nil)(..nil)# => (nil..nil)(nil..nil)# => (nil..nil)

Ranges and Other Classes

An object may be put into a range if its class implements instance method<=>. Ruby core classes that do so includeArray,Complex,File::Stat,Float,Integer,Kernel, Module,Numeric,Rational,String,Symbol, andTime.

Example:

t0 =Time.now# => 2021-09-19 09:22:48.4854986 -0500t1 =Time.now# => 2021-09-19 09:22:56.0365079 -0500t2 =Time.now# => 2021-09-19 09:23:08.5263283 -0500(t0..t2).include?(t1)# => true(t0..t1).include?(t2)# => false

A range can be iterated over only if its elements implement instance methodsucc. Ruby core classes that do so includeInteger,String, andSymbol (but not the other classes mentioned above).

Iterator methods include:

Example:

a = [](1..4).each {|i|a.push(i) }a# => [1, 2, 3, 4]

Ranges and User-Defined Classes

A user-defined class that is to be used in a range must implement instance method<=>; seeInteger#<=>. To make iteration available, it must also implement instance methodsucc; seeInteger#succ.

The class below implements both<=> andsucc, and so can be used both to construct ranges and to iterate over them. Note that theComparable module is included so the== method is defined in terms of<=>.

# Represent a string of 'X' characters.classXsincludeComparableattr_accessor:lengthdefinitialize(n)@length =nenddefsuccXs.new(@length+1)enddef<=>(other)@length<=>other.lengthenddefto_ssprintf"%2d #{inspect}",@lengthenddefinspect'X'*@lengthendendr =Xs.new(3)..Xs.new(6)#=> XXX..XXXXXXr.to_a#=> [XXX, XXXX, XXXXX, XXXXXX]r.include?(Xs.new(5))#=> truer.include?(Xs.new(7))#=> false

What’s Here

First, what’s elsewhere. Class Range:

Here, class Range provides methods that are useful for:

Methods for Creating a Range

Methods for Querying

Methods for Comparing

Methods for Iterating

Methods for Converting

Methods for Working with JSON

To make these methods available:

require'json/add/range'

Public Class Methods

Source
# File ext/json/lib/json/add/range.rb, line 9defself.json_create(object)new(*object['a'])end

Seeas_json.

Source
static VALUErange_initialize(int argc, VALUE *argv, VALUE range){    VALUE beg, end, flags;    rb_scan_args(argc, argv, "21", &beg, &end, &flags);    range_modify(range);    range_init(range, beg, end, RBOOL(RTEST(flags)));    return Qnil;}

Returns a new range based on the given objectsbegin andend. Optional argumentexclude_end determines whether objectend is included as the last object in the range:

Range.new(2,5).to_a# => [2, 3, 4, 5]Range.new(2,5,true).to_a# => [2, 3, 4]Range.new('a','d').to_a# => ["a", "b", "c", "d"]Range.new('a','d',true).to_a# => ["a", "b", "c"]

Public Instance Methods

Source
static VALUErange_percent_step(VALUE range, VALUE step){    return range_step(1, &step, range);}

Same asstep (but doesn’t provide default value forn). The method is convenient for experssive producing ofEnumerator::ArithmeticSequence.

array = [0,1,2,3,4,5,6]# slice each second element:seq = (0..)%2#=> ((0..).%(2))array[seq]#=> [0, 2, 4, 6]# or justarray[(0..)%2]#=> [0, 2, 4, 6]

Note that due to operator precedence in Ruby, parentheses are mandatory around range in this case:

(0..7)%2#=> ((0..7).%(2)) -- as expected0..7%2#=> 0..1 -- parsed as 0..(7 % 2)
Source
static VALUErange_eq(VALUE range, VALUE obj){    if (range == obj)        return Qtrue;    if (!rb_obj_is_kind_of(obj, rb_cRange))        return Qfalse;    return rb_exec_recursive_paired(recursive_equal, range, obj, obj);}

Returnstrue if and only if:

  • other is a range.

  • other.begin == self.begin.

  • other.end == self.end.

  • other.exclude_end? == self.exclude_end?.

Otherwise returnsfalse.

r = (1..5)r== (1..5)# => truer =Range.new(1,5)r=='foo'# => falser== (2..5)# => falser== (1..4)# => falser== (1...5)# => falser==Range.new(1,5,true)# => false

Note that even with the same argument, the return values of== andeql? can differ:

(1..2)== (1..2.0)# => true(1..2).eql? (1..2.0)# => false

Related:Range#eql?.

Source
static VALUErange_eqq(VALUE range, VALUE val){    return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val);}

Returnstrue ifobject is betweenself.begin andself.end.false otherwise:

(1..4)===2# => true(1..4)===5# => false(1..4)==='a'# => false(1..4)===4# => true(1...4)===4# => false('a'..'d')==='c'# => true('a'..'d')==='e'# => false

A case statement uses method===, and so:

case79when (1..50)"low"when (51..75)"medium"when (76..100)"high"end# => "high"case"2.6.5"when..."2.4""EOL"when"2.4"..."2.5""maintenance"when"2.5"..."3.0""stable"when"3.1".."upcoming"end# => "stable"
Source
# File ext/json/lib/json/add/range.rb, line 31defas_json(*)  {JSON.create_id=>self.class.name,'a'=> [first,last,exclude_end? ]  }end

MethodsRange#as_json andRange.json_create may be used to serialize and deserialize a Range object; seeMarshal.

MethodRange#as_json serializesself, returning a 2-element hash representingself:

require'json/add/range'x = (1..4).as_json# => {"json_class"=>"Range", "a"=>[1, 4, false]}y = (1...4).as_json# => {"json_class"=>"Range", "a"=>[1, 4, true]}z = ('a'..'d').as_json# => {"json_class"=>"Range", "a"=>["a", "d", false]}

MethodJSON.create deserializes such a hash, returning a Range object:

Range.json_create(x)# => 1..4Range.json_create(y)# => 1...4Range.json_create(z)# => "a".."d"
Source
static VALUErange_begin(VALUE range){    return RANGE_BEG(range);}

Returns the object that defines the beginning ofself.

(1..4).begin# => 1(..2).begin# => nil

Related:Range#first,Range#end.

Source
static VALUErange_bsearch(VALUE range){    VALUE beg, end, satisfied = Qnil;    int smaller;    /* Implementation notes:     * Floats are handled by mapping them to 64 bits integers.     * Apart from sign issues, floats and their 64 bits integer have the     * same order, assuming they are represented as exponent followed     * by the mantissa. This is true with or without implicit bit.     *     * Finding the average of two ints needs to be careful about     * potential overflow (since float to long can use 64 bits).     *     * The half-open interval (low, high] indicates where the target is located.     * The loop continues until low and high are adjacent.     *     * -1/2 can be either 0 or -1 in C89. However, when low and high are not adjacent,     * the rounding direction of mid = (low + high) / 2 does not affect the result of     * the binary search.     *     * Note that -0.0 is mapped to the same int as 0.0 as we don't want     * (-1...0.0).bsearch to yield -0.0.     */#define BSEARCH(conv, excl) \    do { \        RETURN_ENUMERATOR(range, 0, 0); \        if (!(excl)) high++; \        low--; \        while (low + 1 < high) { \            mid = ((high < 0) == (low < 0)) ? low + ((high - low) / 2) \                : (low + high) / 2; \            BSEARCH_CHECK(conv(mid)); \            if (smaller) { \                high = mid; \            } \            else { \                low = mid; \            } \        } \        return satisfied; \    } while (0)#define BSEARCH_FIXNUM(beg, end, excl) \    do { \        long low = FIX2LONG(beg); \        long high = FIX2LONG(end); \        long mid; \        BSEARCH(INT2FIX, (excl)); \    } while (0)    beg = RANGE_BEG(range);    end = RANGE_END(range);    if (FIXNUM_P(beg) && FIXNUM_P(end)) {        BSEARCH_FIXNUM(beg, end, EXCL(range));    }#if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T)    else if (RB_FLOAT_TYPE_P(beg) || RB_FLOAT_TYPE_P(end)) {        int64_t low  = double_as_int64(NIL_P(beg) ? -HUGE_VAL : RFLOAT_VALUE(rb_Float(beg)));        int64_t high = double_as_int64(NIL_P(end) ?  HUGE_VAL : RFLOAT_VALUE(rb_Float(end)));        int64_t mid;        BSEARCH(int64_as_double_to_num, EXCL(range));    }#endif    else if (is_integer_p(beg) && is_integer_p(end)) {        RETURN_ENUMERATOR(range, 0, 0);        return bsearch_integer_range(beg, end, EXCL(range));    }    else if (is_integer_p(beg) && NIL_P(end)) {        VALUE diff = LONG2FIX(1);        RETURN_ENUMERATOR(range, 0, 0);        while (1) {            VALUE mid = rb_funcall(beg, '+', 1, diff);            BSEARCH_CHECK(mid);            if (smaller) {                if (FIXNUM_P(beg) && FIXNUM_P(mid)) {                    BSEARCH_FIXNUM(beg, mid, false);                }                else {                    return bsearch_integer_range(beg, mid, false);                }            }            diff = rb_funcall(diff, '*', 1, LONG2FIX(2));            beg = mid;        }    }    else if (NIL_P(beg) && is_integer_p(end)) {        VALUE diff = LONG2FIX(-1);        RETURN_ENUMERATOR(range, 0, 0);        while (1) {            VALUE mid = rb_funcall(end, '+', 1, diff);            BSEARCH_CHECK(mid);            if (!smaller) {                if (FIXNUM_P(mid) && FIXNUM_P(end)) {                    BSEARCH_FIXNUM(mid, end, false);                }                else {                    return bsearch_integer_range(mid, end, false);                }            }            diff = rb_funcall(diff, '*', 1, LONG2FIX(2));            end = mid;        }    }    else {        rb_raise(rb_eTypeError, "can't do binary search for %s", rb_obj_classname(beg));    }    return range;}

Returns an element fromself selected by a binary search.

SeeBinary Searching.

Source
static VALUErange_count(int argc, VALUE *argv, VALUE range){    if (argc != 0) {        /* It is odd for instance (1...).count(0) to return Infinity. Just let         * it loop. */        return rb_call_super(argc, argv);    }    else if (rb_block_given_p()) {        /* Likewise it is odd for instance (1...).count {|x| x == 0 } to return         * Infinity. Just let it loop. */        return rb_call_super(argc, argv);    }    VALUE beg = RANGE_BEG(range), end = RANGE_END(range);    if (NIL_P(beg) || NIL_P(end)) {        /* We are confident that the answer is Infinity. */        return DBL2NUM(HUGE_VAL);    }    if (is_integer_p(beg)) {        VALUE size = range_size(range);        if (!NIL_P(size)) {            return size;        }    }    return rb_call_super(argc, argv);}

Returns the count of elements, based on an argument or block criterion, if given.

With no argument and no block given, returns the number of elements:

(1..4).count# => 4(1...4).count# => 3('a'..'d').count# => 4('a'...'d').count# => 3(1..).count# => Infinity(..4).count# => Infinity

With argumentobject, returns the number ofobject found inself, which will usually be zero or one:

(1..4).count(2)# => 1(1..4).count(5)# => 0(1..4).count('a')# => 0

With a block given, calls the block with each element; returns the number of elements for which the block returns a truthy value:

(1..4).count {|element|element<3 }# => 2

Related:Range#size.

Source
static VALUErange_cover(VALUE range, VALUE val){    VALUE beg, end;    beg = RANGE_BEG(range);    end = RANGE_END(range);    if (rb_obj_is_kind_of(val, rb_cRange)) {        return RBOOL(r_cover_range_p(range, beg, end, val));    }    return r_cover_p(range, beg, end, val);}

Returnstrue if the given argument is withinself,false otherwise.

With non-range argumentobject, evaluates with<= and<.

For rangeself with included end value (#exclude_end? == false), evaluates thus:

self.begin<=object<=self.end

Examples:

r = (1..4)r.cover?(1)# => truer.cover?(4)# => truer.cover?(0)# => falser.cover?(5)# => falser.cover?('foo')# => falser = ('a'..'d')r.cover?('a')# => truer.cover?('d')# => truer.cover?(' ')# => falser.cover?('e')# => falser.cover?(0)# => false

For ranger with excluded end value (#exclude_end? == true), evaluates thus:

r.begin<=object<r.end

Examples:

r = (1...4)r.cover?(1)# => truer.cover?(3)# => truer.cover?(0)# => falser.cover?(4)# => falser.cover?('foo')# => falser = ('a'...'d')r.cover?('a')# => truer.cover?('c')# => truer.cover?(' ')# => falser.cover?('d')# => falser.cover?(0)# => false

With range argumentrange, compares the first and last elements ofself andrange:

r = (1..4)r.cover?(1..4)# => truer.cover?(0..4)# => falser.cover?(1..5)# => falser.cover?('a'..'d')# => falser = (1...4)r.cover?(1..3)# => truer.cover?(1..4)# => false

If begin and end are numeric,cover? behaves likeinclude?

(1..3).cover?(1.5)# => true(1..3).include?(1.5)# => true

But when not numeric, the two methods may differ:

('a'..'d').cover?('cc')# => true('a'..'d').include?('cc')# => false

Returnsfalse if either:

  • The begin value ofself is larger than its end value.

  • An internal call to<=> returnsnil; that is, the operands are not comparable.

Beginless ranges cover all values of the same type before the end, excluding the end for exclusive ranges. Beginless ranges cover ranges that end before the end of the beginless range, or at the end of the beginless range for inclusive ranges.

(..2).cover?(1)# => true(..2).cover?(2)# => true(..2).cover?(3)# => false(...2).cover?(2)# => false(..2).cover?("2")# => false(..2).cover?(..2)# => true(..2).cover?(...2)# => true(..2).cover?(.."2")# => false(...2).cover?(..2)# => false

Endless ranges cover all values of the same type after the beginning. Endless exclusive ranges do not cover endless inclusive ranges.

(2..).cover?(1)# => false(2..).cover?(3)# => true(2...).cover?(3)# => true(2..).cover?(2)# => true(2..).cover?("2")# => false(2..).cover?(2..)# => true(2..).cover?(2...)# => true(2..).cover?("2"..)# => false(2...).cover?(2..)# => false(2...).cover?(3...)# => true(2...).cover?(3..)# => false(3..).cover?(2..)# => false

Ranges that are both beginless and endless cover all values and ranges, and return true for all arguments, with the exception that beginless and endless exclusive ranges do not cover endless inclusive ranges.

(nil...).cover?(Object.new)# => true(nil...).cover?(nil...)# => true(nil..).cover?(nil...)# => true(nil...).cover?(nil..)# => false(nil...).cover?(1..)# => false

Related:Range#include?.

Source
static VALUErange_each(VALUE range){    VALUE beg, end;    long i;    RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_size);    beg = RANGE_BEG(range);    end = RANGE_END(range);    if (FIXNUM_P(beg) && NIL_P(end)) {        range_each_fixnum_endless(beg);    }    else if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */        return range_each_fixnum_loop(beg, end, range);    }    else if (RB_INTEGER_TYPE_P(beg) && (NIL_P(end) || RB_INTEGER_TYPE_P(end))) {        if (SPECIAL_CONST_P(end) || RBIGNUM_POSITIVE_P(end)) { /* end >= FIXNUM_MIN */            if (!FIXNUM_P(beg)) {                if (RBIGNUM_NEGATIVE_P(beg)) {                    do {                        rb_yield(beg);                    } while (!FIXNUM_P(beg = rb_big_plus(beg, INT2FIX(1))));                    if (NIL_P(end)) range_each_fixnum_endless(beg);                    if (FIXNUM_P(end)) return range_each_fixnum_loop(beg, end, range);                }                else {                    if (NIL_P(end)) range_each_bignum_endless(beg);                    if (FIXNUM_P(end)) return range;                }            }            if (FIXNUM_P(beg)) {                i = FIX2LONG(beg);                do {                    rb_yield(LONG2FIX(i));                } while (POSFIXABLE(++i));                beg = LONG2NUM(i);            }            ASSUME(!FIXNUM_P(beg));            ASSUME(!SPECIAL_CONST_P(end));        }        if (!FIXNUM_P(beg) && RBIGNUM_SIGN(beg) == RBIGNUM_SIGN(end)) {            if (EXCL(range)) {                while (rb_big_cmp(beg, end) == INT2FIX(-1)) {                    rb_yield(beg);                    beg = rb_big_plus(beg, INT2FIX(1));                }            }            else {                VALUE c;                while ((c = rb_big_cmp(beg, end)) != INT2FIX(1)) {                    rb_yield(beg);                    if (c == INT2FIX(0)) break;                    beg = rb_big_plus(beg, INT2FIX(1));                }            }        }    }    else if (SYMBOL_P(beg) && (NIL_P(end) || SYMBOL_P(end))) { /* symbols are special */        beg = rb_sym2str(beg);        if (NIL_P(end)) {            rb_str_upto_endless_each(beg, sym_each_i, 0);        }        else {            rb_str_upto_each(beg, rb_sym2str(end), EXCL(range), sym_each_i, 0);        }    }    else {        VALUE tmp = rb_check_string_type(beg);        if (!NIL_P(tmp)) {            if (!NIL_P(end)) {                rb_str_upto_each(tmp, end, EXCL(range), each_i, 0);            }            else {                rb_str_upto_endless_each(tmp, each_i, 0);            }        }        else {            if (!discrete_object_p(beg)) {                rb_raise(rb_eTypeError, "can't iterate from %s",                         rb_obj_classname(beg));            }            if (!NIL_P(end))                range_each_func(range, each_i, 0);            else                for (;; beg = rb_funcallv(beg, id_succ, 0, 0))                    rb_yield(beg);        }    }    return range;}

With a block given, passes each element ofself to the block:

a = [](1..4).each {|element|a.push(element) }# => 1..4a# => [1, 2, 3, 4]

Raises an exception unlessself.first.respond_to?(:succ).

With no block given, returns an enumerator.

Source
static VALUErange_end(VALUE range){    return RANGE_END(range);}

Returns the object that defines the end ofself.

(1..4).end# => 4(1...4).end# => 4(1..).end# => nil

Related:Range#begin,Range#last.

Alias for:to_a
Source
static VALUErange_eql(VALUE range, VALUE obj){    if (range == obj)        return Qtrue;    if (!rb_obj_is_kind_of(obj, rb_cRange))        return Qfalse;    return rb_exec_recursive_paired(recursive_eql, range, obj, obj);}

Returnstrue if and only if:

  • other is a range.

  • other.begin.eql?(self.begin).

  • other.end.eql?(self.end).

  • other.exclude_end? == self.exclude_end?.

Otherwise returnsfalse.

r = (1..5)r.eql?(1..5)# => truer =Range.new(1,5)r.eql?('foo')# => falser.eql?(2..5)# => falser.eql?(1..4)# => falser.eql?(1...5)# => falser.eql?(Range.new(1,5,true))# => false

Note that even with the same argument, the return values of== andeql? can differ:

(1..2)== (1..2.0)# => true(1..2).eql? (1..2.0)# => false

Related:Range#==.

Source
static VALUErange_exclude_end_p(VALUE range){    return RBOOL(EXCL(range));}

Returnstrue ifself excludes its end value;false otherwise:

Range.new(2,5).exclude_end?# => falseRange.new(2,5,true).exclude_end?# => true(2..5).exclude_end?# => false(2...5).exclude_end?# => true
Source
static VALUErange_first(int argc, VALUE *argv, VALUE range){    VALUE n, ary[2];    if (NIL_P(RANGE_BEG(range))) {        rb_raise(rb_eRangeError, "cannot get the first element of beginless range");    }    if (argc == 0) return RANGE_BEG(range);    rb_scan_args(argc, argv, "1", &n);    ary[0] = n;    ary[1] = rb_ary_new2(NUM2LONG(n));    rb_block_call(range, idEach, 0, 0, first_i, (VALUE)ary);    return ary[1];}

With no argument, returns the first element ofself, if it exists:

(1..4).first# => 1('a'..'d').first# => "a"

With non-negative integer argumentn given, returns the firstn elements in an array:

(1..10).first(3)# => [1, 2, 3](1..10).first(0)# => [](1..4).first(50)# => [1, 2, 3, 4]

Raises an exception if there is no first element:

(..4).first# Raises RangeError
Source
static VALUErange_hash(VALUE range){    st_index_t hash = EXCL(range);    VALUE v;    hash = rb_hash_start(hash);    v = rb_hash(RANGE_BEG(range));    hash = rb_hash_uint(hash, NUM2LONG(v));    v = rb_hash(RANGE_END(range));    hash = rb_hash_uint(hash, NUM2LONG(v));    hash = rb_hash_uint(hash, EXCL(range) << 24);    hash = rb_hash_end(hash);    return ST2FIX(hash);}

Returns the integer hash value forself. Two range objectsr0 andr1 have the same hash value if and only ifr0.eql?(r1).

Related:Range#eql?,Object#hash.

Returnstrue ifobject is an element ofself,false otherwise:

(1..4).include?(2)# => true(1..4).include?(5)# => false(1..4).include?(4)# => true(1...4).include?(4)# => false('a'..'d').include?('b')# => true('a'..'d').include?('e')# => false('a'..'d').include?('B')# => false('a'..'d').include?('d')# => true('a'...'d').include?('d')# => false

If begin and end are numeric,include? behaves likecover?

(1..3).include?(1.5)# => true(1..3).cover?(1.5)# => true

But when not numeric, the two methods may differ:

('a'..'d').include?('cc')# => false('a'..'d').cover?('cc')# => true

Related:Range#cover?.

Alias for:member?
Source
static VALUErange_inspect(VALUE range){    return rb_exec_recursive(inspect_range, range, 0);}

Returns a string representation ofself, includingbegin.inspect andend.inspect:

(1..4).inspect# => "1..4"(1...4).inspect# => "1...4"(1..).inspect# => "1.."(..4).inspect# => "..4"

Note that returns fromto_s andinspect may differ:

('a'..'d').to_s# => "a..d"('a'..'d').inspect# => "\"a\"..\"d\""

Related:Range#to_s.

Source
static VALUErange_last(int argc, VALUE *argv, VALUE range){    if (NIL_P(RANGE_END(range))) {        rb_raise(rb_eRangeError, "cannot get the last element of endless range");    }    if (argc == 0) return RANGE_END(range);    if (integer_end_optimizable(range)) {        return rb_int_range_last(argc, argv, range);    }    return rb_ary_last(argc, argv, rb_Array(range));}

With no argument, returns the last element ofself, if it exists:

(1..4).last# => 4('a'..'d').last# => "d"

Note thatlast with no argument returns the end element ofself even ifexclude_end? istrue:

(1...4).last# => 4('a'...'d').last# => "d"

With non-negative integer argumentn given, returns the lastn elements in an array:

(1..10).last(3)# => [8, 9, 10](1..10).last(0)# => [](1..4).last(50)# => [1, 2, 3, 4]

Note thatlast with argument does not return the end element ofself ifexclude_end? ittrue:

(1...4).last(3)# => [1, 2, 3]('a'...'d').last(3)# => ["a", "b", "c"]

Raises an exception if there is no last element:

(1..).last# Raises RangeError
Source
static VALUErange_max(int argc, VALUE *argv, VALUE range){    VALUE e = RANGE_END(range);    int nm = FIXNUM_P(e) || rb_obj_is_kind_of(e, rb_cNumeric);    if (NIL_P(RANGE_END(range))) {        rb_raise(rb_eRangeError, "cannot get the maximum of endless range");    }    VALUE b = RANGE_BEG(range);    if (rb_block_given_p() || (EXCL(range) && !nm)) {        if (NIL_P(b)) {            rb_raise(rb_eRangeError, "cannot get the maximum of beginless range with custom comparison method");        }        return rb_call_super(argc, argv);    }    else if (argc) {        VALUE ary[2];        ID reverse_each;        CONST_ID(reverse_each, "reverse_each");        rb_scan_args(argc, argv, "1", &ary[0]);        ary[1] = rb_ary_new2(NUM2LONG(ary[0]));        rb_block_call(range, reverse_each, 0, 0, first_i, (VALUE)ary);        return ary[1];#if 0        if (integer_end_optimizable(range)) {            return rb_int_range_last(argc, argv, range, true);        }        return rb_ary_reverse(rb_ary_last(argc, argv, rb_Array(range)));#endif    }    else {        int c = NIL_P(b) ? -1 : OPTIMIZED_CMP(b, e);        if (c > 0)            return Qnil;        if (EXCL(range)) {            if (!RB_INTEGER_TYPE_P(e)) {                rb_raise(rb_eTypeError, "cannot exclude non Integer end value");            }            if (c == 0) return Qnil;            if (!NIL_P(b) && !RB_INTEGER_TYPE_P(b)) {                rb_raise(rb_eTypeError, "cannot exclude end value with non Integer begin value");            }            if (FIXNUM_P(e)) {                return LONG2NUM(FIX2LONG(e) - 1);            }            return rb_int_minus(e,INT2FIX(1));        }        return e;    }}

Returns the maximum value inself, using method<=> or a given block for comparison.

With no argument and no block given, returns the maximum-valued element ofself.

(1..4).max# => 4('a'..'d').max# => "d"(-4..-1).max# => -1

With non-negative integer argumentn given, and no block given, returns then maximum-valued elements ofself in an array:

(1..4).max(2)# => [4, 3]('a'..'d').max(2)# => ["d", "c"](-4..-1).max(2)# => [-1, -2](1..4).max(50)# => [4, 3, 2, 1]

If a block is given, it is called:

  • First, with the first two element ofself.

  • Then, sequentially, with the so-far maximum value and the next element ofself.

To illustrate:

(1..4).max {|a,b|p [a,b];a<=>b }# => 4

Output:

[2,1][3,2][4,3]

With no argument and a block given, returns the return value of the last call to the block:

(1..4).max {|a,b|-(a<=>b) }# => 1

With non-negative integer argumentn given, and a block given, returns the return values of the lastn calls to the block in an array:

(1..4).max(2) {|a,b|-(a<=>b) }# => [1, 2](1..4).max(50) {|a,b|-(a<=>b) }# => [1, 2, 3, 4]

Returns an empty array ifn is zero:

(1..4).max(0)# => [](1..4).max(0) {|a,b|-(a<=>b) }# => []

Returnsnil or an empty array if:

  • The begin value of the range is larger than the end value:

    (4..1).max# => nil(4..1).max(2)# => [](4..1).max {|a,b|-(a<=>b) }# => nil(4..1).max(2) {|a,b|-(a<=>b) }# => []
  • The begin value of an exclusive range is equal to the end value:

    (1...1).max# => nil(1...1).max(2)# => [](1...1).max  {|a,b|-(a<=>b) }# => nil(1...1).max(2)  {|a,b|-(a<=>b) }# => []

Raises an exception if either:

  • self is a endless range:(1..).

  • A block is given andself is a beginless range.

Related:Range#min,Range#minmax.

Source
Also aliased as:include?
Source
static VALUErange_min(int argc, VALUE *argv, VALUE range){    if (NIL_P(RANGE_BEG(range))) {        rb_raise(rb_eRangeError, "cannot get the minimum of beginless range");    }    if (rb_block_given_p()) {        if (NIL_P(RANGE_END(range))) {            rb_raise(rb_eRangeError, "cannot get the minimum of endless range with custom comparison method");        }        return rb_call_super(argc, argv);    }    else if (argc != 0) {        return range_first(argc, argv, range);    }    else {        VALUE b = RANGE_BEG(range);        VALUE e = RANGE_END(range);        int c = NIL_P(e) ? -1 : OPTIMIZED_CMP(b, e);        if (c > 0 || (c == 0 && EXCL(range)))            return Qnil;        return b;    }}

Returns the minimum value inself, using method<=> or a given block for comparison.

With no argument and no block given, returns the minimum-valued element ofself.

(1..4).min# => 1('a'..'d').min# => "a"(-4..-1).min# => -4

With non-negative integer argumentn given, and no block given, returns then minimum-valued elements ofself in an array:

(1..4).min(2)# => [1, 2]('a'..'d').min(2)# => ["a", "b"](-4..-1).min(2)# => [-4, -3](1..4).min(50)# => [1, 2, 3, 4]

If a block is given, it is called:

  • First, with the first two element ofself.

  • Then, sequentially, with the so-far minimum value and the next element ofself.

To illustrate:

(1..4).min {|a,b|p [a,b];a<=>b }# => 1

Output:

[2,1][3,1][4,1]

With no argument and a block given, returns the return value of the last call to the block:

(1..4).min {|a,b|-(a<=>b) }# => 4

With non-negative integer argumentn given, and a block given, returns the return values of the lastn calls to the block in an array:

(1..4).min(2) {|a,b|-(a<=>b) }# => [4, 3](1..4).min(50) {|a,b|-(a<=>b) }# => [4, 3, 2, 1]

Returns an empty array ifn is zero:

(1..4).min(0)# => [](1..4).min(0) {|a,b|-(a<=>b) }# => []

Returnsnil or an empty array if:

  • The begin value of the range is larger than the end value:

    (4..1).min# => nil(4..1).min(2)# => [](4..1).min {|a,b|-(a<=>b) }# => nil(4..1).min(2) {|a,b|-(a<=>b) }# => []
  • The begin value of an exclusive range is equal to the end value:

    (1...1).min# => nil(1...1).min(2)# => [](1...1).min  {|a,b|-(a<=>b) }# => nil(1...1).min(2)  {|a,b|-(a<=>b) }# => []

Raises an exception if either:

  • self is a beginless range:(..4).

  • A block is given andself is an endless range.

Related:Range#max,Range#minmax.

Source
static VALUErange_minmax(VALUE range){    if (rb_block_given_p()) {        return rb_call_super(0, NULL);    }    return rb_assoc_new(        rb_funcall(range, id_min, 0),        rb_funcall(range, id_max, 0)    );}

Returns a 2-element array containing the minimum and maximum value inself, either according to comparison method<=> or a given block.

With no block given, returns the minimum and maximum values, using<=> for comparison:

(1..4).minmax# => [1, 4](1...4).minmax# => [1, 3]('a'..'d').minmax# => ["a", "d"](-4..-1).minmax# => [-4, -1]

With a block given, the block must return an integer:

  • Negative ifa is smaller thanb.

  • Zero ifa andb are equal.

  • Positive ifa is larger thanb.

The block is calledself.size times to compare elements; returns a 2-elementArray containing the minimum and maximum values fromself, per the block:

(1..4).minmax {|a,b|-(a<=>b) }# => [4, 1]

Returns[nil, nil] if:

  • The begin value of the range is larger than the end value:

    (4..1).minmax# => [nil, nil](4..1).minmax {|a,b|-(a<=>b) }# => [nil, nil]
  • The begin value of an exclusive range is equal to the end value:

    (1...1).minmax# => [nil, nil](1...1).minmax  {|a,b|-(a<=>b) }# => [nil, nil]

Raises an exception ifself is a beginless or an endless range.

Related:Range#min,Range#max.

Source
static VALUErange_overlap(VALUE range, VALUE other){    if (!rb_obj_is_kind_of(other, rb_cRange)) {        rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Range)",                 rb_class_name(rb_obj_class(other)));    }    VALUE self_beg = RANGE_BEG(range);    VALUE self_end = RANGE_END(range);    int self_excl = EXCL(range);    VALUE other_beg = RANGE_BEG(other);    VALUE other_end = RANGE_END(other);    int other_excl = EXCL(other);    if (empty_region_p(self_beg, other_end, other_excl)) return Qfalse;    if (empty_region_p(other_beg, self_end, self_excl)) return Qfalse;    if (!NIL_P(self_beg) && !NIL_P(other_beg)) {        VALUE cmp = rb_funcall(self_beg, id_cmp, 1, other_beg);        if (NIL_P(cmp)) return Qfalse;        /* if both begin values are equal, no more comparisons needed */        if (rb_cmpint(cmp, self_beg, other_beg) == 0) return Qtrue;    }    else if (NIL_P(self_beg) && !NIL_P(self_end) && NIL_P(other_beg) && !NIL_P(other_end)) {        VALUE cmp = rb_funcall(self_end, id_cmp, 1, other_end);        return RBOOL(!NIL_P(cmp));    }    if (empty_region_p(self_beg, self_end, self_excl)) return Qfalse;    if (empty_region_p(other_beg, other_end, other_excl)) return Qfalse;    return Qtrue;}

Returnstrue ifrange overlaps withself,false otherwise:

(0..2).overlap?(1..3)#=> true(0..2).overlap?(3..4)#=> false(0..).overlap?(..0)#=> true

With non-range argument, raisesTypeError.

(1..3).overlap?(1)# TypeError

Returnsfalse if an internal call to<=> returnsnil; that is, the operands are not comparable.

(1..3).overlap?('a'..'d')# => false

Returnsfalse ifself orrange is empty. “Empty range” means that its begin value is larger than, or equal for an exclusive range, its end value.

(4..1).overlap?(2..3)# => false(4..1).overlap?(..3)# => false(4..1).overlap?(2..)# => false(2...2).overlap?(1..2)# => false(1..4).overlap?(3..2)# => false(..4).overlap?(3..2)# => false(1..).overlap?(3..2)# => false(1..2).overlap?(2...2)# => false

Returnsfalse if the begin value one ofself andrange is larger than, or equal if the other is an exclusive range, the end value of the other:

(4..5).overlap?(2..3)# => false(4..5).overlap?(2...4)# => false(1..2).overlap?(3..4)# => false(1...3).overlap?(3..4)# => false

Returnsfalse if the end value one ofself andrange is larger than, or equal for an exclusive range, the end value of the other:

(4..5).overlap?(2..3)# => false(4..5).overlap?(2...4)# => false(1..2).overlap?(3..4)# => false(1...3).overlap?(3..4)# => false

Note that the method wouldn’t make any assumptions about the beginless range being actually empty, even if its upper bound is the minimum possible value of its type, so all this would returntrue:

(...-Float::INFINITY).overlap?(...-Float::INFINITY)# => true(..."").overlap?(..."")# => true(...[]).overlap?(...[])# => true

Even if those ranges are effectively empty (no number can be smaller than-Float::INFINITY), they are still considered overlapping with themselves.

Related:Range#cover?.

Source
static VALUErange_reverse_each(VALUE range){    RETURN_SIZED_ENUMERATOR(range, 0, 0, range_enum_reverse_size);    VALUE beg = RANGE_BEG(range);    VALUE end = RANGE_END(range);    int excl = EXCL(range);    if (NIL_P(end)) {        rb_raise(rb_eTypeError, "can't iterate from %s",                 rb_obj_classname(end));    }    if (FIXNUM_P(beg) && FIXNUM_P(end)) {        if (excl) {            if (end == LONG2FIX(FIXNUM_MIN)) return range;            end = rb_int_minus(end, INT2FIX(1));        }        range_reverse_each_fixnum_section(beg, end);    }    else if ((NIL_P(beg) || RB_INTEGER_TYPE_P(beg)) && RB_INTEGER_TYPE_P(end)) {        if (excl) {            end = rb_int_minus(end, INT2FIX(1));        }        range_reverse_each_positive_bignum_section(beg, end);        range_reverse_each_fixnum_section(beg, end);        range_reverse_each_negative_bignum_section(beg, end);    }    else {        return rb_call_super(0, NULL);    }    return range;}

With a block given, passes each element ofself to the block in reverse order:

a = [](1..4).reverse_each {|element|a.push(element) }# => 1..4a# => [4, 3, 2, 1]a = [](1...4).reverse_each {|element|a.push(element) }# => 1...4a# => [3, 2, 1]

With no block given, returns an enumerator.

Source
static VALUErange_size(VALUE range){    VALUE b = RANGE_BEG(range), e = RANGE_END(range);    if (RB_INTEGER_TYPE_P(b)) {        if (rb_obj_is_kind_of(e, rb_cNumeric)) {            return ruby_num_interval_step_size(b, e, INT2FIX(1), EXCL(range));        }        if (NIL_P(e)) {            return DBL2NUM(HUGE_VAL);        }    }    if (!discrete_object_p(b)) {        CANT_ITERATE_FROM(b);    }    return Qnil;}

Returns the count of elements inself if both begin and end values are numeric; otherwise, returnsnil:

(1..4).size# => 4(1...4).size# => 3(1..).size# => Infinity('a'..'z').size# => nil

Ifself is not iterable, raises an exception:

(0.5..2.5).size# TypeError(..1).size# TypeError

Related:Range#count.

Source
static VALUErange_step(int argc, VALUE *argv, VALUE range){    VALUE b, e, v, step;    int c, dir;    b = RANGE_BEG(range);    e = RANGE_END(range);    v = b;    const VALUE b_num_p = rb_obj_is_kind_of(b, rb_cNumeric);    const VALUE e_num_p = rb_obj_is_kind_of(e, rb_cNumeric);    // For backward compatibility reasons (conforming to behavior before 3.4), String/Symbol    // supports both old behavior ('a'..).step(1) and new behavior ('a'..).step('a')    // Hence the additional conversion/additional checks.    const VALUE str_b = rb_check_string_type(b);    const VALUE sym_b = SYMBOL_P(b) ? rb_sym2str(b) : Qnil;    if (rb_check_arity(argc, 0, 1))        step = argv[0];    else {        if (b_num_p || !NIL_P(str_b) || !NIL_P(sym_b) || (NIL_P(b) && e_num_p))            step = INT2FIX(1);        else            rb_raise(rb_eArgError, "step is required for non-numeric ranges");    }    const VALUE step_num_p = rb_obj_is_kind_of(step, rb_cNumeric);    if (step_num_p && b_num_p && rb_equal(step, INT2FIX(0))) {        rb_raise(rb_eArgError, "step can't be 0");    }    if (!rb_block_given_p()) {        // This code is allowed to create even beginless ArithmeticSequence, which can be useful,        // e.g., for array slicing:        //   ary[(..-1) % 3]        if (step_num_p && ((b_num_p && (NIL_P(e) || e_num_p)) || (NIL_P(b) && e_num_p))) {            return rb_arith_seq_new(range, ID2SYM(rb_frame_this_func()), argc, argv,                    range_step_size, b, e, step, EXCL(range));        }        // ...but generic Enumerator from beginless range is useless and probably an error.        if (NIL_P(b)) {            rb_raise(rb_eArgError, "#step for non-numeric beginless ranges is meaningless");        }        RETURN_SIZED_ENUMERATOR(range, argc, argv, 0);    }    if (NIL_P(b)) {        rb_raise(rb_eArgError, "#step iteration for beginless ranges is meaningless");    }    if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(step)) {        /* perform summation of numbers in C until their reach Fixnum limit */        long i = FIX2LONG(b), unit = FIX2LONG(step);        do {            rb_yield(LONG2FIX(i));            i += unit;          /* FIXABLE+FIXABLE never overflow */        } while (FIXABLE(i));        b = LONG2NUM(i);        /* then switch to Bignum API */        for (;; b = rb_big_plus(b, step))            rb_yield(b);    }    else if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) {        /* fixnums are special: summation is performed in C for performance */        long end = FIX2LONG(e);        long i, unit = FIX2LONG(step);        if (unit < 0) {            if (!EXCL(range))                end -= 1;            i = FIX2LONG(b);            while (i > end) {                rb_yield(LONG2NUM(i));                i += unit;            }        }        else {            if (!EXCL(range))                end += 1;            i = FIX2LONG(b);            while (i < end) {                rb_yield(LONG2NUM(i));                i += unit;            }        }    }    else if (b_num_p && step_num_p && ruby_float_step(b, e, step, EXCL(range), TRUE)) {        /* done */    }    else if (!NIL_P(str_b) && FIXNUM_P(step)) {        // backwards compatibility behavior for String only, when no step/Integer step is passed        // See discussion in https://bugs.ruby-lang.org/issues/18368        VALUE iter[2] = {INT2FIX(1), step};        if (NIL_P(e)) {            rb_str_upto_endless_each(str_b, step_i, (VALUE)iter);        }        else {            rb_str_upto_each(str_b, e, EXCL(range), step_i, (VALUE)iter);        }    }    else if (!NIL_P(sym_b) && FIXNUM_P(step)) {        // same as above: backward compatibility for symbols        VALUE iter[2] = {INT2FIX(1), step};        if (NIL_P(e)) {            rb_str_upto_endless_each(sym_b, sym_step_i, (VALUE)iter);        }        else {            rb_str_upto_each(sym_b, rb_sym2str(e), EXCL(range), sym_step_i, (VALUE)iter);        }    }    else if (NIL_P(e)) {        // endless range        for (;; v = rb_funcall(v, id_plus, 1, step))            rb_yield(v);    }    else if (b_num_p && step_num_p && r_less(step, INT2FIX(0)) < 0) {        // iterate backwards, for consistency with ArithmeticSequence        if (EXCL(range)) {            for (; r_less(e, v) < 0; v = rb_funcall(v, id_plus, 1, step))                rb_yield(v);        }        else {            for (; (c = r_less(e, v)) <= 0; v = rb_funcall(v, id_plus, 1, step)) {                rb_yield(v);                if (!c) break;            }        }    }    else if ((dir = r_less(b, e)) == 0) {        if (!EXCL(range)) {            rb_yield(v);        }    }    else if (dir == r_less(b, rb_funcall(b, id_plus, 1, step))) {        // Direction of the comparison. We use it as a comparison operator in cycle:        // if begin < end, the cycle performs while value < end (iterating forward)        // if begin > end, the cycle performs while value > end (iterating backward with        // a negative step)        // One preliminary addition to check the step moves iteration in the same direction as        // from begin to end; otherwise, the iteration should be empty.        if (EXCL(range)) {            for (; r_less(v, e) == dir; v = rb_funcall(v, id_plus, 1, step))                rb_yield(v);        }        else {            for (; (c = r_less(v, e)) == dir || c == 0; v = rb_funcall(v, id_plus, 1, step)) {                rb_yield(v);                if (!c) break;            }        }    }    return range;}

Iterates over the elements of range in steps ofs. The iteration is performed by+ operator:

(0..6).step(2) {puts_1 }#=> 1..5# Prints: 0, 2, 4, 6# Iterate between two dates in step of 1 day (24 hours)(Time.utc(2022,2,24)..Time.utc(2022,3,1)).step(24*60*60) {puts_1 }# Prints:#   2022-02-24 00:00:00 UTC#   2022-02-25 00:00:00 UTC#   2022-02-26 00:00:00 UTC#   2022-02-27 00:00:00 UTC#   2022-02-28 00:00:00 UTC#   2022-03-01 00:00:00 UTC

If + step decreases the value, iteration is still performed when stepbegin is higher than theend:

(0..6).step(-2) {puts_1 }# Prints nothing(6..0).step(-2) {puts_1 }# Prints: 6, 4, 2, 0(Time.utc(2022,3,1)..Time.utc(2022,2,24)).step(-24*60*60) {puts_1 }# Prints:#   2022-03-01 00:00:00 UTC#   2022-02-28 00:00:00 UTC#   2022-02-27 00:00:00 UTC#   2022-02-26 00:00:00 UTC#   2022-02-25 00:00:00 UTC#   2022-02-24 00:00:00 UTC

When the block is not provided, and range boundaries and step areNumeric, the method returnsEnumerator::ArithmeticSequence.

(1..5).step(2)# => ((1..5).step(2))(1.0..).step(1.5)#=> ((1.0..).step(1.5))(..3r).step(1/3r)#=> ((..3/1).step((1/3)))

Enumerator::ArithmeticSequence can be further used as a value object for iteration or slicing of collections (seeArray#[]). There is a convenience method% with behavior similar tostep to produce arithmetic sequences more expressively:

# Same as (1..5).step(2)(1..5)%2# => ((1..5).%(2))

In a generic case, when the block is not provided,Enumerator is returned:

('a'..).step('b')#=> #<Enumerator: "a"..:step("b")>('a'..).step('b').take(3)#=> ["a", "ab", "abb"]

Ifs is not provided, it is considered1 for ranges with numericbegin:

(1..5).step {p_1 }# Prints: 1, 2, 3, 4, 5

For non-Numeric ranges, step absence is an error:

(Time.utc(2022,3,1)..Time.utc(2022,2,24)).step {p_1 }# raises: step is required for non-numeric ranges (ArgumentError)

For backward compatibility reasons,String ranges support the iteration both with string step and with integer step. In the latter case, the iteration is performed by calculating the next values withString#succ:

('a'..'e').step(2) {p_1 }# Prints: a, c, e('a'..'e').step {p_1 }# Default step 1; prints: a, b, c, d, e
Source
static VALUErange_to_a(VALUE range){    if (NIL_P(RANGE_END(range))) {        rb_raise(rb_eRangeError, "cannot convert endless range to an array");    }    return rb_call_super(0, 0);}

Returns an array containing the elements inself, if a finite collection; raises an exception otherwise.

(1..4).to_a# => [1, 2, 3, 4](1...4).to_a# => [1, 2, 3]('a'..'d').to_a# => ["a", "b", "c", "d"]
Also aliased as:entries
Source
# File ext/json/lib/json/add/range.rb, line 51defto_json(*args)as_json.to_json(*args)end

Returns aJSON string representingself:

require'json/add/range'puts (1..4).to_jsonputs (1...4).to_jsonputs ('a'..'d').to_json

Output:

{"json_class":"Range","a":[1,4,false]}{"json_class":"Range","a":[1,4,true]}{"json_class":"Range","a":["a","d",false]}
Source
static VALUErange_to_s(VALUE range){    VALUE str, str2;    str = rb_obj_as_string(RANGE_BEG(range));    str2 = rb_obj_as_string(RANGE_END(range));    str = rb_str_dup(str);    rb_str_cat(str, "...", EXCL(range) ? 3 : 2);    rb_str_append(str, str2);    return str;}

Returns a string representation ofself, includingbegin.to_s andend.to_s:

(1..4).to_s# => "1..4"(1...4).to_s# => "1...4"(1..).to_s# => "1.."(..4).to_s# => "..4"

Note that returns fromto_s andinspect may differ:

('a'..'d').to_s# => "a..d"('a'..'d').inspect# => "\"a\"..\"d\""

Related:Range#inspect.