Reputation: 3968
Given is an object with a bunch of conditions:
var conditions = {
even: function (i) { return i % 2 == 0; },
greatherThan: function (i) { return i > 10; },
inValidRange: function (i) {
return i > 20 && i < 100;
}
};
and an array numbers in a range starting from 0 up to 39: var numbers = _.range(0, 40);
.
I want to filter numbers
by every condition. I used underscore.js to do this:
var result = _.filter(numbers, function(current) {
return _.all(_.values(conditions), function(f) {
return f(current);
});
});
// returns [ 22, 24, 26, 28, 30, 32, 34, 36, 38 ]
It works fine, but unfortunately, the code above looks weird and it is pretty confusing.
How do I simplify this code in order to make it more readable and understandable?
Upvotes: 3
Views: 377
Reputation: 664970
This helper function might be useful:
_.mixin({
invokeWith: function() {
var args = arguments;
return function(fn) {
return fn.apply(null, args);
};
}
});
It also would be better if your conditions
was an array right away. You can either use an array of named functions:
var conditions = [
function even(i) { return i % 2 == 0; },
function greaterThan(i) { return i > 10; },
function inValidRange(i) { return i > settings.validRange.from && i < settings.validRange.to; }
];
or transform it before, so that we can omit (and don't need to repeatedly call) the values
function:
conditions = _.values(conditions);
Now you can shorten your code to
var result = _.filter(numbers, function(current) {
return _.all(conditions, _.invokeWith(current));
});
It might be more complicated to understand what happens because most people would have to look up that invokeWith
function, but the concept is easier to grasp since the code is quite declarative, containing all verbs to build the natural expression invoke all conditions with current
.
If one wants to remove the outer lambda expression as well, it would get a little shorter but harder to understand:
// functional programming FTW :-)
var result = _.filter(numbers, _.compose(_.partial(_.all, conditions), _.invokeWith));
Upvotes: 4
Reputation: 141877
There is no need to rewrite that code; put it in a named function:
function filterByConditions(values, conditions){
return _.filter(values, function(current) {
return _.all(_.values(conditions), function(f) {
return f(current);
});
});
}
The code within the function is not that complex, and shouldn't confuse anyone familiar with underscore.js. If you deem it necessary than you can add comments to the body of the function. Either way, when you use the function elsewhere it should be clear what the function does from the name:
var result = filterByConditions(numbers, conditions);
If you want to simplify the body of the function, so that developers who are not familiar with underscore.js can still read it you can replace _all
and _values
with a simple for ... in loop. Most Javascript developers can infer what _.filter does without ever having used underscore.js:
function filterByConditions(values, conditions){
return _.filter(values, function(current){
for(condition_name in conditions){
if(!conditions[condition_name](current)){
// Condition failed
return false;
}
}
// All conditions passed
return true;
});
}
Upvotes: 0