Skip to content

Instantly share code, notes, and snippets.

@FGRibreau
Created September 21, 2012 09:28
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save FGRibreau/3760576 to your computer and use it in GitHub Desktop.
Save FGRibreau/3760576 to your computer and use it in GitHub Desktop.
My yesterday night 1 hour code rush: JavaScript Hash with Range-based keys
/**
* September 20 night: 1 hour code rush
* @FGRibreau
*
* Requirements
* ------------
* The aim of this code rush is to enable developers to define a "range hash map"
* in a very expressive way (without if/then/else).
*
* Data-set
* --------
*
* rules =
* Nb of days -> Precision levels set
* ---------------------------------------
* ] -∞ ; 1 ] -> {min, hour, day, mon}
* ] 1 ; 8 ] -> {hour, day, mon}
* ] 8 ; 62 ] -> {day, mon}
* ] 62 ; +∞ [ -> {mon}
*
*
* High-level API
* --------------
*
* rules.getFor([days.number]) -> {}
*
* Note
* ----
*
* - Only _.toArray() underscore method was used and it could easily be replaced by a Array::slice.call().
* - The unit-tests use Qunit.
*/
var rules = new Rules()
.add(range('] -∞ ; 1 ]'), precisions('min', 'hour', 'day', 'mon'))
.add(range('] 1 ; 8 ]'), precisions('hour', 'day', 'mon'))
.add(range('] 8 ; 62 ]'), precisions('day', 'mon'))
.add(range('] 62 ; +∞ ['), precisions('mon'));
/**
* Implementation
*/
function Rules(){
var rules = [];
this.add = function(range, value){
if(!range.in){throw new Error("add(range, value), range must implement .in(Number) -> Boolean");}
rules.push(_.toArray(arguments));
return this;
};
this.getFor = function(n){
var i = -1, iM = rules.length;
while(i++ < iM){
if(rules[i][0].in(n)){return rules[i][1];}
}
return undefined;
};
};
function range(str){
function car(s){return (s || '')[0];}
function cdr(s){return (s || '').substring(1);}
function cdl(s){return (s || '').substring(0, s.length-1);}
function last(s){return (s || '')[s.length-1];}
function toNumber(s){return s.indexOf('∞')>0 ? (s[0]==='-'?-1:1)/0 : parseInt(s, 10);}
function _range(){
var L, R, Lincluded, Rincluded;
str = str.replace(/\s+/gi, '').split(';');
this.Lincluded = Lincluded = car(str[0]) === '[';
this.L = L = toNumber(cdr(str[0]));
this.R = R = toNumber(cdl(str[1]));
this.Rincluded = Rincluded = last(str[1]) === ']';
this.in = function(n){
return (
(!Lincluded && n > L) || (Lincluded && n >= L)) && ((!Rincluded && n < R) || (Rincluded && n <= R)
);
};
}
return new _range();
};
function precisions(){return _.toArray(arguments);};
/**
* Unit-tests
*/
// Test
function checkRangeParse(str, Lincluded, L, R, Rincluded){
var _range = range(str);
equal(_range.Lincluded, Lincluded, str + ' Lincluded =' + Lincluded);
equal(_range.L, L, str + ' L =' + L);
equal(_range.R, R, str + ' R =' + R);
equal(_range.Rincluded, Rincluded, str + ' Rincluded =' + Rincluded);
}
checkRangeParse('] -∞ ; 1 ]', false, -Infinity, 1, true);
checkRangeParse('] 1 ; 8 ]', false, 1 , 8 , true);
checkRangeParse('] 8 ; 62 ]', false, 8 , 62, true);
checkRangeParse('] 62 ; +∞ [', false, 62, +Infinity, false);
// Test
function inRange(_range, _in, expected){
equal(range(_range).in(_in), expected, _range + '.in('+_in+') = ' + expected);
}
inRange('] -∞ ; 1 ]', -10, true);
inRange('] -∞ ; 1 ]', 0, true);
inRange('] -∞ ; 1 ]', 1, true);
inRange('] -∞ ; 1 ]', 2, false);
inRange('] 1 ; 8 ]', 1, false);
inRange('] 1 ; 8 ]', 2, true);
inRange('] 1 ; 8 ]', 8, true);
inRange('] 1 ; 8 ]', 9, false);
inRange('] 8 ; 62 ]', 8, false);
inRange('] 8 ; 62 ]', 9, true);
inRange('] 8 ; 62 ]', 62, true);
inRange('] 8 ; 62 ]', 63, false);
inRange('] 62 ; +∞ [', 61, false);
inRange('] 62 ; +∞ [', 62, false);
inRange('] 62 ; +∞ [', 63, true);
inRange('] 62 ; +∞ [', 890, true);
// Test
function valueFor(_range, expected){
deepEqual(rules.getFor(_range), expected, "rules.getFor("+_range+") " + JSON.stringify(expected));
}
valueFor(-1, ['min', 'hour', 'day', 'mon']);
valueFor(1, ['min', 'hour', 'day', 'mon']);
valueFor(2, ['hour', 'day', 'mon']);
valueFor(8, ['hour', 'day', 'mon']);
valueFor(9, ['day', 'mon']);
valueFor(62, ['day', 'mon']);
valueFor(63, ['mon']);
valueFor(6390, ['mon']);
@FGRibreau
Copy link
Author

Feel free to comment & improve !

#L54 .getFor() currently just perform a linear search and should be improved by sorting the ranges - as well as looking for overlapping range - at insert time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment