In this case, you're better off with forEach
, but I address your question about why !predicate
didn't work (and how you can make something like it that does) below as well.
First, the simple forEach
solution:
Prosiac:
function test(array, predicate){
var filterTrue = [];
var filterFalse = [];
array.forEach(function(value) {
if (predicate(value)) {
filterTrue.push(value);
} else {
filterFalse.push(value);
}
});
// rest of method
}
A bit more terse:
function test(array, predicate){
var filterTrue = [];
var filterFalse = [];
array.forEach(function(value) {
(predicate(value) ? filterTrue : filterFalse).push(value);
});
// rest of method
}
As an aside, I have tried:
var filterFalse = array.filter(!predicate);
But this is doesn't seem to work for reasons I'm still trying to understand
It would have to be:
var filterFalse = array.filter(function(entry) {
return !predicate(entry);
});
...and that would indeed work, but it means you're making two passes through the array and calling the predicate twice for every element. That's why I recommended forEach
: Only one pass through the array is required, and only one call to the predicate for each entry.
The reason your var filterFalse = array.filter(!predicate);
didn't work is that it's taking the predicate
variable, which contains a reference to a function, and logically inverting it with !
. The logically-inverted version of a non-null object reference (functions are objects) is false
, so you were in effect passing false
into filter
.
More completely: The unary !
coerces its operand to a boolean value and then returns the opposite of it (false
for true
and true
for false
). So !predicate
will result in false
for any value of predicate
that coerces to true
(aka "truthy" values), and will result in true
for any value of predicate
that coerces to false
(aka "falsey" values). So what are the "truthy" and "falsey" values? The "falsey" values are 0
, ""
, null
, undefined
, NaN
, and of course, false
; the "truthy" values are all others, including all non-null
object references.
If you program with predicates a lot and want a way to say "not predicate" and get a function that gives you the inverted result, you can do that like this:
function not(predicate) {
return function() {
return !predicate.apply(this, arguments);
};
}
and then:
var filterFalse = array.filter(not(predicate));
That not
function works like this: It returns a new function that, when called, will call the predicate function you gave it, passing along the this
value it was called with and all of the arguments it was called with (via Function#apply
— spec | MDN), logically inverting the return value it gets from the predicate, and then returning that inverted value.
But again, using that would require two passes through the array. Sometimes with high-level abstractions, though, that could be preferable to the forEach
solution.
Finally, if you do this "either/or" thing a lot, you could of course make a function to do that:
function divvyUp(array, predicate) {
var filterTrue = [], filterFalse = [];
array.forEach(function(value) {
(predicate(value) ? filterTrue : filterFalse).push(value);
});
return {
filterTrue: filterTrue,
filterFalse: filterFalse
};
}