f_underscore.js | |
---|---|
| (function() { |
Baseline | |
All of | var f_ = function(property) { return chain(property); }; |
Export | var root = this;
if (typeof exports !== 'undefined') {
var _ = require('underscore');
exports.f_ = f_;
} else {
this['f_'] = f_;
var _ = this['_'];
} |
Current version. | f_.VERSION = '0.1.0'; |
Shorthand for underscore's identity function. | f_.I =
f_.i = _.identity; |
Given two arrays of same length, one with strings/keys, the other with values, return an object comprised of the keys and values. | f_.zipObject = function(keys, props) {
var pairs = _.zip(keys,props),
result = {};
_.each(pairs, function(pair) {
result[pair[0]] = pair[1];
});
return result;
}; |
Given a function | f_.partial = function(fn) {
var args = _.rest(arguments, 1);
return function() {
return fn.apply(this, args.concat([].slice.apply(arguments)));
};
}; |
Given a list of functions, return a new function that, when invoked, will
call each function in the list in succession passing the return value of each
as an argument to the next. The reverse call order of | f_.thread = function() {
var args = arguments;
if(_.isArray(args[0])) {
args = args[0];
}
return function() {
var val = [].slice.apply(arguments);
for(var i = 0; i < args.length; i++) {
val = [args[i].apply(this, val)];
}
return val[0];
};
};
f_.callFunction = function(obj) {
return function(fn) {
return fn(obj);
};
}; |
Iterator Function Generators | |
Turn a non-function value into a function that returns that value. | f_.functionize = function(f_v) {
return _.isFunction(f_v) ? f_v : function(){ return f_v; };
}; |
Accessors | |
Make a function that will return the specified property when invoked with an object. | f_.get = function(prop) {
return function(obj) {
return obj[prop];
};
}; |
Get a property, use it to call the iterator. Idea: use an object hash to getSet multiple properties | f_.getSet = function(prop, iterator) {
return function(obj) {
obj[prop] = iterator(obj[prop]);
return obj;
};
}; |
Make a function that will return the value of the specified object's property when invoked with a property. | f_.getByProperty = function(obj) {
return function(prop) {
return obj[prop];
};
}; |
Make a function that will set the specified property to be the evaluation
of | f_.set = function(prop, f_v) {
var iterator = f_.functionize(f_v);
return function(obj) {
obj[prop] = iterator(obj);
return obj;
};
}; |
Make a function that can be called with property names. It will set
the property of the specified object to the evaluation of | f_.setByProperty = function(obj, f_v) {
var iterator = f_.functionize(f_v);
return function(prop) {
obj[prop] = iterator(obj, prop);
return obj;
};
}; |
Make a function that when called with an object will return a
new object with only the specified | f_.project = function(properties) {
return function(obj) {
return f_.zipObject(properties,
_.map(properties, f_.getByProperty(obj)));
};
}; |
Expressions | |
Unary Expression Template | f_.unaryExpr = function(expr, f_v) {
if(f_v === undefined) {
f_v = f_.i;
}
if(_.isFunction(f_v)) {
return function(obj) {
return expr(f_v(obj));
};
} else {
return function(obj) {
return expr(f_v);
};
};
}; |
Binary Expression Template | f_.binaryExpr = function(expr, f_v_l, f_v_r) { |
Support single arg variants where we're partially applying the right hand value and using identity as left hand value. Useful for operations on lists of values rather than objects. i.e. .map([1,2], f.add(1)) -> [2,3]; | if(f_v_r === undefined) {
f_v_r = f_v_l;
f_v_l = f_.i;
}
if(_.isFunction(f_v_l)) {
if(_.isFunction(f_v_r)) {
return function(obj) {
return expr(f_v_l(obj), f_v_r(obj));
};
} else {
return function(obj) {
return expr(f_v_l(obj), f_v_r);
};
}
} else {
if(_.isFunction(f_v_r)) {
return function(obj) {
return expr(f_v_l, f_v_r(obj));
};
} else {
return function() {
return expr(f_v_l, f_v_r);
};
}
}
}; |
Method Expr | f_.methodExpr = function(method) {
var args = _.map(_.rest(arguments), f_.functionize);
return function(obj) {
var argVals = _.map(args, function(fn) { return fn(obj); }); |
Support calling method in a binary expression sense | if(!obj[method]) {
obj = _.first(argVals);
argVals = _.rest(argVals);
}
return obj[method].apply(obj, argVals);
};
}; |
Primitives | var |
Arithmetic Primitives | add = function(l, r) { return l + r; },
subtract = function(l, r) { return l - r; },
multiply = function(l, r) { return l * r; },
divide = function(l, r) { return l / r; },
modulo = function(l, r) { return l % r; },
increment = function(l) { return l + 1; },
decrement = function(l) { return l - 1; },
square = function(l) { return l * l; },
negate = function(l) { return l * -1; }, |
String | append = function(l, r) { return (''+l).concat(r); }
prepend = function(l, r) { return (''+r).concat(l); } |
Relational | greaterThan = function(l, r) { return l > r; },
atLeast = function(l, r) { return l >= r; },
lessThan = function(l, r) { return l < r; },
atMost = function(l, r) { return l <= r; },
greaterOf = function(l, r) { return l > r ? l : r; },
lesserOf = function(l, r) { return l < r ? l : r; }, |
Equality | equality = _.isEqual,
inequality = function() { return !equality.apply(this,arguments); }, |
Logical | and = function(l, r) { return l && r; },
neither = function(l, r) { return !l && !r; },
or = function(l, r) { return l || r; },
xor = function(l, r) { return (l && !r) || (!l && r); },
not = function(l) { return !l; }
; |
Expression Iterators | |
Unary | f_.incr = f_.increment = f_.partial(f_.unaryExpr, increment);
f_.decr = f_.decrement = f_.partial(f_.unaryExpr, decrement);
f_.sqr = f_.square = f_.partial(f_.unaryExpr, square);
f_.not = f_.partial(f_.unaryExpr, not);
f_.neg = f_.negate = f_.partial(f_.unaryExpr, negate); |
Binary | f_.add = f_.partial(f_.binaryExpr, add);
f_.sub = f_.subtract = f_.partial(f_.binaryExpr, subtract);
f_.mul = f_.multiply = f_.partial(f_.binaryExpr, multiply);
f_.div = f_.divide = f_.partial(f_.binaryExpr, divide);
f_.mod = f_.modulo = f_.partial(f_.binaryExpr, modulo); |
String | f_.append = f_.partial(f_.binaryExpr, append);
f_.prepend = f_.partial(f_.binaryExpr, prepend); |
Relational | f_.eq = f_.isEqual = f_.partial(f_.binaryExpr, equality);
f_.neq = f_.isNotEqual = f_.partial(f_.binaryExpr, inequality);
f_.gt = f_.greaterThan = f_.partial(f_.binaryExpr, greaterThan);
f_.gte = f_.atLeast = f_.partial(f_.binaryExpr, atLeast);
f_.lt = f_.lessThan = f_.partial(f_.binaryExpr, lessThan);
f_.lte = f_.atMost = f_.partial(f_.binaryExpr, atMost);
f_.greaterOf = f_.partial(f_.binaryExpr, greaterOf);
f_.lesserOf = f_.partial(f_.binaryExpr, lesserOf); |
Logical | f_.and = f_.partial(f_.binaryExpr, and);
f_.neither = f_.partial(f_.binaryExpr, neither);
f_.or = f_.partial(f_.binaryExpr, or);
f_.xor = f_.partial(f_.binaryExpr, xor); |
String/Array Method Iterators | var methods = [ |
Array | 'pop',
'push',
'reverse',
'shift',
'sort',
'splice',
'unshift',
'join', |
String | 'charAt',
'charCodeAt',
'fromCharCode',
'match',
'replace',
'search',
'split',
'substr',
'substring',
'toLowerCase',
'toUpperCase', |
Common | 'concat',
'indexOf',
'lastIndexOf',
'slice'
];
_.extend(f_, f_.zipObject(
methods,
_.map(methods, function(fn) {
return f_.partial(f_.methodExpr, fn);
})
)
); |
Chaining | |
The object we'll extend for chaining. | var _chainBase = {};
_.each(_.functions(f_), function(key) {
_chainBase[key] = function() {
this._stack.push(f_[key].apply(this, arguments));
return this;
};
}); |
Setup our chained iterator function. | var chain = function(get) {
var _stack = [];
if(get !== undefined) {
_stack.push(f_.get(get));
}
var chained = function() {
return f_.thread(_stack).apply(this, arguments);
};
chained._stack = _stack; |
Would be great to eliminate this next step. Thoughts on how this could be done more efficiently? | return _.extend(chained, _chainBase);
};
}).call(this);
|