Reputation: 580
I'm re-creating functions from the underscore library but I'm running into a roadblock while trying to implement the _.reject()
function. For the purposes of this question, I'll include the code I've written for three functions: _.each()
, _.filter()
, and _.reject()
.
_.each = function(collection, iterator) {
if (Array.isArray(collection)) {
for (var i = 0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for (var i in collection) {
iterator(collection[i], i, collection);
}
}
};
_.filter = function(collection, test) {
var results = [];
_.each(collection, function(i) {
if (test(i)) {
results.push(i);
}
})
return results;
};
And here's the code for the function that I'm getting a problem with, the _.reject()
method, along with the isEven()
function that I'm passing in as the test
argument.
_.reject = function(collection, test) {
return _.filter(collection, !test);
};
var isEven = function(x) {
if (x % 2 === 0) return true;
return false;
};
According to MDN's page on Expressions and Operators, the Logical NOT (!) operator Returns false if its single operand can be converted to true; otherwise, returns true.
But when I run the following code _.reject([1,2,3], isEven)
I get an error saying that test is not a function
. Why am I unable to use the !
operator while invoking a function (e.g., _.filter([1,2,3], !isEven)
)?
Upvotes: 3
Views: 2068
Reputation: 7573
You cannot negate a function. It's a meaningless thing to do (unless you really want the value false
without typing it). Instead you want to negate the function's return value. I suspect what you want is something like so
_.reject = function(collection, test) {
return _.filter(collection, function(e) { return !test(e); });
}
Upvotes: 3
Reputation: 816364
Why am I unable to use the
!
operator while invoking a function (e.g.,_.filter([1,2,3], !isEven))
?
Note that you are actually not invoking isEven
, you are merely referencing it. As you said, !
"Returns false if its single operand can be converted to true; otherwise, returns true."
isEven
is a reference to a function, i.e. an object. Objects convert to true
, hence !test
results in false
:
_.filter([1,2,3], false))
Now you are passing a Boolean instead of a function to _.filter
, hence the error message "test is not a function".
Instead, you have to pass a function that negates the result of the test function:
_.filter([1,2,3], function() {
return !test.apply(this, arguments);
});
Upvotes: 7
Reputation: 1074158
When you refer to a function, rather than calling it, you're referring to the function's object reference:
function foo() {
alert("Hi there");
}
var f = foo; // <== Getting the function's reference, not calling it
f(); // <== Now we call it
So !isEven
would be negating the function reference. Since isEven
is a non-null
reference, it's truthy; and so !isEven
is false
. Not what you want. :-)
Your reject
could be written with a function that calls test
and inverts its return value:
_.reject = function(collection, test) {
return _.filter(collection, function(e) { return !test(e); });
};
Or if you want to go the functional programming approach, you can write a function that, when called, will return a new function that negates the return value:
function not(f) {
return function() {
return !f.apply(this, arguments);
};
}
Then anywhere you want to invert a callback's meaning, you'd just use invert
:
_.reject = function(collection, test) {
return _.filter(collection, not(test));
};
Upvotes: 7