Skip to content

Instantly share code, notes, and snippets.

@thunder9
Created September 7, 2012 14:06
Show Gist options
  • Save thunder9/3666528 to your computer and use it in GitHub Desktop.
Save thunder9/3666528 to your computer and use it in GitHub Desktop.
A Ruby-like "range" for JavaScript
// A Ruby-like "range" for JavaScript
// https://github.com/thunder9
//
// Require: https://github.com/thunder9/rb-enumerable.js
(function (global) {
if (!global.RubyLike.enumerable || !global.RubyLike.Enumerator) {
throw Error('rb-enumerable.js required');
}
var rl = global.RubyLike;
var Range = function (first, last, exclude_end) {
if (!(this instanceof Range)) {
return new Range(first, last, exclude_end);
}
var enm = rl.Enumerator();
if (isnum(first) && isnum(last)) {
var source;
if (exclude_end) {
source = function (yielder) {
for (var val = first; val < last; val++) {
yielder(val);
}
};
} else {
source = function (yielder) {
for (var val = first; val <= last; val++) {
yielder(val);
}
};
}
enm = rl.Enumerator(source);
}
if (isfunc(first.succ) && isfunc(first.spaceship)) {
var n = exclude_end ? -1 : 0;
enm = rl.Enumerator(function (yielder) {
for (var val = first; val.spaceship(last) <= n; val = val.succ()) {
yielder(val);
}
});
}
this.each = function (yielder) {
if (isfunc(yielder)) {
enm.each(function (val) { yielder(val); });
return this;
} else {
return enm;
}
};
var getters = {
first: { get: function () { return first; } },
last: { get: function () { return last; } },
exclude_end: { get: function () { return exclude_end; } }
};
if (isfunc(Object.defineProperties)) {
Object.defineProperties(this, getters);
} else {
this.get_first = getters.first.get
this.get_last = getters.last.get
this.get_exclude_end = getters.exclude_end.get
}
};
var RangeProto = function () {
this.cover = function (obj) {
var r = get_param(this);
if (isnum(obj) && isnum(r.first) && isnum(r.last)) {
if (r.exclude_end) {
return obj >= r.first && obj < r.last;
} else {
return obj >= r.first && obj <= r.last;
}
}
if (isfunc(obj.spaceship)) {
return obj.spaceship(r.first) === 1
&& obj.spaceship(r.last) <= (r.exclude_end ? -1 : 0);
}
return false;
};
this.member = function (obj) {
var r = get_param(this);
if (isnum(r.first) && isnum(r.last)) {
return this.cover(obj);
} else {
return rl.enumerable.member.call(this, obj);
}
return false
},
this.include = this.member;
this.step = function (s, block) {
if (!isnum(s)) { s = 1; }
var counted = 0;
var enm = rl.Enumerator(this, function (yielder, val) {
if (counted++ % s === 0) {
yielder(val);
}
});
if (isfunc(block)) {
enm.each(function (val) { block(val); });
return this;
} else {
return enm;
}
};
this.eql = function (other) {
var r = get_param(this);
var o = get_param(other);
if (other instanceof Range && r.exclude_end === o.exclude_end) {
if (isnum(r.first) && isnum(r.last)
&& isnum(o.first) && isnum(o.last)) {
return r.first === o.first && r.last === o.last;
} else if (isfunc(r.first.eql) && isfunc(r.last.eql)) {
return r.first.eql(o.first) && r.last.eql(o.last);
}
}
return false;
};
this.begin = this.first;
this.end = this.last;
}
var classof = function (obj) {
if (obj === null) return 'Null';
if (obj === undefined) return 'Undefined';
return Object.prototype.toString.call(obj).slice(8, -1);
};
var isnum = function (obj) {
return classof(obj) === 'Number';
};
var isfunc = function (obj) {
return classof(obj) === 'Function';
}
var get_param = function (range) {
if (isfunc(Object.defineProperties)) {
return range;
} else {
return {
first: range.get_first(),
last: range.get_last(),
exclude_end: range.get_exclude_end()
};
}
};
RangeProto.prototype = rl.enumerable;
Range.prototype = new RangeProto();
rl.Range = Range;
} (this));
<!DOCTYPE html>
<html>
<head>
<script src="https://raw.github.com/thunder9/rb-enumerable.js/master/rb-enumerable.js"></script>
<script src="rb-range.js"></script>
</head>
<body>
<pre id="out"></pre>
<script>
var p = function (o) {
var out = document.getElementById('out');
p = function (o) {
out.innerHTML += '// ' + JSON.stringify(o);
out.appendChild(document.createElement('br'));
}
p(o);
};
var rl = RubyLike;
if (typeof Object.defineProperties === 'function') {
rl.iteration_limit = 1000; //ES5
} else {
rl.set_iteration_limit(1000);
}
p(rl.Range(-5,10).map(function (x) { return x*2; }).to_a());
p(rl.Range(-5,10,true).map(function (x) { return x*2; }).to_a());
var Int = function (val) {
if (!(this instanceof Int)) {
return new Int(val);
}
this.val = val;
this.succ = function () { return Int(this.val + 1); };
this.spaceship = function (other) {
if (this.val === other.val) { return 0; }
else if (this.val > other.val) { return 1; }
else { return -1; }
};
this.eql = function (other) {
return this.spaceship(other) === 0;
}
};
p(rl.Range(Int(-5),Int(10)).map(function (x) { return x.val*2; }).to_a());
p(rl.Range(Int(-5),Int(10),true).map(function (x) { return x.val*2; }).to_a());
p(rl.Range(-5,10).step(3).to_a());
p(rl.Range(-5,10,true).step(3).to_a());
p(rl.Range(-5,10).cover(6));
p(rl.Range(-5,10).cover(-5000));
p(rl.Range(Int(-5),Int(10)).cover(Int(6)));
p(rl.Range(Int(-5),Int(10)).cover(Int(6.5)));
p(rl.Range(Int(-5),Int(10)).cover(Int(-5000)));
p(rl.Range(Int(-5),Int(10)).member(Int(6)));
p(rl.Range(Int(-5),Int(10)).member(Int(6.5)));
p(rl.Range(-5,10).eql(rl.Range(-5,10)));
p(rl.Range(-5,10).eql(rl.Range(-5,11)));
p(rl.Range(Int(-5),Int(10)).eql(rl.Range(Int(-5),Int(10))));
p(rl.Range(Int(-5),Int(10)).eql(rl.Range(Int(-5),Int(11))));
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment