Range

The JS.Range class is used to represent intervals – sequences with a start and end value. It is directly based on Ruby’s Range class. A Range may be constructed using integers, strings, or any type of object that responds to the succ() method. Ranges are a lightweight way to represent sequences of objects, and as collections they respond to all the Enumerable methods.

A range is constructed using a start and end value, and an optional flag that indicates whether the end value is included when iterating.

new JS.Range(1,5)           // -> 1,2,3,4,5
new JS.Range(4,8,true)      // -> 4,5,6,7

new JS.Range('a','d')       // -> 'a','b','c','d'
new JS.Range('B','G',true)  // -> 'B','C','D','E','F'

The Range object only stores the start and endpoints, not the intermediate values: these are generated using succ() when iterating. For example, here’s a quick class that implements enough of an API to be used as a Range delimiter. We need succ() to return the next object in the sequence, and compareTo() to allow a Range to determine whether a given object is within the range:

var NumberWrapper = new JS.Class({
    initialize: function(value) {
        this._value = value;
    },

    compareTo: function(object) {
        var a = this._value, b = object._value;
        return a < b ? -1 : (a > b ? 1 : 0);
    },

    succ: function() {
        return new this.klass(this._value + 1);
    },

    inspect: function() {
        return '#<NumberWrapper:' + this._value + '>';
    }
});

We can use this class in a Range and iterating will generate the intermediate objects:

var nums = new JS.Range(new NumberWrapper(16),
                        new NumberWrapper(24),
                        true);

nums.forEach(function(number) {
    console.log(number.inspect());
});

// -> #<NumberWrapper:16>
//    #<NumberWrapper:17>
//    #<NumberWrapper:18>
//    #<NumberWrapper:19>
//    #<NumberWrapper:20>
//    #<NumberWrapper:21>
//    #<NumberWrapper:22>
//    #<NumberWrapper:23>

The full Range object API is listed below. Ranges also respond to all the Enumerable methods based on the forEach() method.

begin()

Returns the start value of the Range.

forEach(block, context)

Calls block with each item in the Range in turn. context is optional and specifies the binding of this within the block function.

// Calls the function with
// arguments 1,2,3,4
new JS.Range(1,4).forEach(function(number) {
    // ...
});

end()

Returns the end value of the Range.

equals(other)

Returns true iff other is a Range with the same start and end values and the same value for excludesEnd(). Note that the ranges new JS.Range(1,4) and new JS.Range(1,5,true) are not equal.

excludesEnd()

Returns true iff the Range excludes its end value during iteration.

first()

Returns the start value of the Range.

includes(item)

Returns true iff item is contained in the Range, that is if it is between the start and end values of the range.

new JS.Range(1,4).includes(3)         // -> true
new JS.Range(2,6,true).includes(6)    // -> false

Note that an object may be considered to be included in a range even though it does not appear during iteration and may even lie outside the iteration range. For example the following expression is true as 8.5 is less than 9:

new JS.Range(6,9,true).includes(8.5)   // -> true

Aliased as covers(), member() and match(), so a Range may be used as the argument to Enumerable#grep.

last()

Returns the end value of the Range.

step(n, block, context)

Iterates over every nth item in the Range, calling block with each. Returns an Enumerator if called with no block.

new JS.Range('G','V').step(5).entries()
    // -> ["G", "L", "Q", "V"]