Created
September 7, 2012 14:06
-
-
Save thunder9/3666528 to your computer and use it in GitHub Desktop.
A Ruby-like "range" for JavaScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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