Skip to content

Instantly share code, notes, and snippets.

@samshull
Created November 6, 2012 17:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samshull/4026270 to your computer and use it in GitHub Desktop.
Save samshull/4026270 to your computer and use it in GitHub Desktop.
ranges that support unicode characters and dates (sort of)
(function() {
var StopIterator = this.StopIterator || function() { Error.apply(this, arguments); };
function Range(/*[start, ] stop [, step [, exclusive]]*/) {
var args = Array.prototype.slice.call(arguments, 0);
if (!(this instanceof Range)) return Range.apply(new Range(1), args);
switch(args.length) {
case 0:
throw new Error('Cannot create a Range without at least a maximum limit');
case 1:
args[1] = args[0];
args[0] = 0;
case 2:
args[2] = 1;
args[3] = 1;
case 3:
args[4] = false;
case 4:
if (isNaN(parseFloat(args[2]))) {
throw new Error('The third argument of Range must be a number instead found ' + typeof(args[2]));
}
default:
this._start = args[0];
this._stop = args[1];
this._step = Math.abs(parseFloat(args[2]));
this._exclusive = !!arguments[3];
}
// check that the range values are valid
Range.succ(this._start);
Range.succ(this._stop);
return this;
}
Range.succ = function(object, step) {
step = step || 1;
switch(Object.prototype.toString.call(object)) {
case '[object Date]':
return new Date(object.valueOf() + step);
case '[object Number]':
return object + step;
case '[object String]':
return String.fromCharCode(object.charCodeAt(0) + step);
default:
if ("next" in object) {
return object.next(step);
}
throw new Error('Bad value for range');
}
};
Range.equal = function(x, y) {
if (x === null || x === undefined || y === null || y === undefined) {
return x === y;
}
switch(Object.prototype.toString.call(x)) {
case '[object Array]':
case '[object Object]':
if (Object.prototype.toString.call(y) === Object.prototype.toString.call(x)) {
for (var p in x) {
if (!(p in y) || !this.equal(x[p], y[p])) {
return false;
}
}
return true;
}
return false;
case '[object Date]':
return x.valueOf() === y.valueOf();
default:
return x.valueOf() === y.valueOf();
}
};
Range.prototype.toArray = function() {
return this._array = this._array || this.getArray();
};
Range.prototype.getArray = function(step, exclusive) {
var start = this._start,
stop = this._stop,
result = [],
hold = null;
step = Math.abs(step || this._step);
exclusive = exclusive === undefined ? this._exclusive : !!exclusive;
if (stop < start) {
hold = start;
start = stop;
stop = hold;
}
while (start < stop) {
result[result.length] = start;
start = Range.succ(start, step);
}
if (!exclusive) {
result[result.length] = stop;
}
return hold !== null ? result.reverse() : result;
};
Range.prototype.indexOf = function(object, offset) {
var array = this.toArray();
offset = Math.floor(offset || 0);
for (var length = array.length; offset < length; ++offset) {
if (Range.equal(array[offset], object)) {
return offset;
}
}
return -1;
};
Range.prototype.includes = function(object) {
return this.indexOf(object) > -1;
};
Range.prototype.covers = function(object) {
return this.min() <= object && this.max() >= object;
};
Range.prototype.first = function() {
return this._start;
};
Range.prototype.last = function() {
return this._stop;
};
Range.prototype.end = function() {
if (!this._exclusive) {
return this._stop;
}
var array = this.toArray();
return array[array.length - 1];
};
Range.prototype.max = function() {
return this.start < this._stop ? this._stop : null;
};
Range.prototype.min = function() {
return this.start < this._stop ? this._start : null;
};
Range.prototype.step = function(step, callback) {
var iterator = this.__iterator__(step), next;
if (!callback) {
return iterator;
}
try {
while (next = iterator.next()) {
if (callback.call(this, next) === false) {
break;
}
}
} catch(e) {
if (!(e instanceof StopIterator)) {
throw e;
}
}
return this;
};
Range.prototype.each = function(callback) {
return this.step(null, callback);
};
Range.prototype.__iterator__ = function(step) {
return new RangeIterator(this, step);
}
function RangeIterator(range, step) {
this._range = range;
this._array = range.getArray(step);
this._index = 0;
}
RangeIterator.prototype.next = function() {
if (this._index < this._array.length) {
return this._array[this._index++];
}
throw new StopIterator();
};
this.Range = Range;
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment