3

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))?

3
  • Sounds like perhaps you want !test() instead. Negating a function is meaningless. Negating a function's return value is more useful. Commented Sep 22, 2015 at 15:43
  • Ah, I thought that negating the function a la !isEven would negate the return value. Commented Sep 22, 2015 at 15:45
  • I've posted an answer that may provide a solution. let me know how that goes. Commented Sep 22, 2015 at 15:46

3 Answers 3

7

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)); }; 
Sign up to request clarification or add additional context in comments.

Comments

7

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); }); 

Comments

3

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); }); } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.