0
  _.filter = function(collection, test) {
    var result = [];
    _.each(collection, function(value) {
      if(test(value)) {
        result.push(value);
      }
    })
    return result;
  };
  _.reject = function(collection, test) {
    var result = [];
    return _.filter(collection, function(value) {
      return !test(value);
    })
  };

I'm a bit puzzled by how this works. I have two underscore.js functions defined here in the same scope. If I pass a test array of random nums how does _.filter in _.reject work?

var isEven = function(num) { return num % 2 === 0; };
var odds = _.reject([1, 2, 3, 4, 5, 6], isEven);
expect(odds).to.eql([1, 3, 5]);

For example I test my functions and I get the assert is true but I don't understand how this works

2 Answers 2

1

Reject is just reusing some logic that filter accomplishes. It could easily have been written this way:

_.reject = function(collection, test) {
  var result = [];
  _.each(collection, function(value) {
    if(!test(value)) {
      result.push(value);
    }
  })
  return result;
};

You will notice that the only thing difference between filter and reject is whether we want to keep items when the test is true, or when the test is false.

// Creates the function reject
_.reject = function(collection, test) {
  var result = [];

  // Calls filter but hands in a custom callback.
  // Filter iterates each item in the list, and keeps it
  // if test(item) returns true (test is our custom flipResult).
  // Remember that reject does the opposite, it keeps it
  // if test(item) returns false (aka !true).
  return _.filter(collection, function flipResult(value) {

    // The user gave us a test method. We want to keep items
    // that return false when passed to test.
    // If we call _.filter(collection, test) we will get all
    // items that return true when passed to test, but thats not what we want
    // So instead of handing test in directly, we hand in our custom callback
    // The custom callback calls test and flips the result.
    // This means filter will keep all items that return false
    // because our custom callback is flipping the result !test
    return !test(value);
  })
};
Sign up to request clarification or add additional context in comments.

Comments

1

For example, if your array of numbers are:

const nums = [1, 2, 3, 4, 5, 6]

and you have a function isEven which takes a number and returns true if it is even and false if it is not even (ie odd):

function isEven(num) {
  return num % 2 === 0;
}

Then running _.filter(arr, isEven) will perform the following logic:

  • arr takes the name of collection and isEven takes the name of test
  • Declare an array called result.
  • Loop through every number in even

    • For each number (value), check if calling test(value) gives a result of true.
    • If test(value) is true, then add the number (value) to the end of the result array.
    • Go to next number in the array collection
  • return the result array.

So, filter() executes the isEven function for each number in your array, and if it returns true, it is added to a new array (ie: it is kept).

_.reject() does the opposite to _.filter() - that is, it keeps all elements the callback function returns false for. As you know _.filter() keeps values it returns true for, you can use _.filter() by negating the value of the boolean returned by test(value). This way, when test(value) returns false, you negate it to be true, making the _.filter() method keep that element. If test(value) returns true, then you will negate that to be false, making the filter method discard of that value.

4 Comments

Thank you. That makes a lot of sense. One thing I'm still confused about is how this executes: return _.filter(collection, function(value) { return !test(value); }). How does it plug in the function(value) in for the filter function for the test parameter? I guess what I'm asking is how does the function parameter play into the filter function? Since instead of returning when test is true it returns when false. So does the function(value) execute after each time that function filter does for every element?
@NewAtLearningThis it is executed when you call your _.reject() function. So you call your function, it first creates the var result = []; (which isn't used) and then it returns the result of calling _.filter(). Remeber when you call filter, it will execute the function passed on ever element in your array and only keep those which that function returns true for. _.filter() will result in an array, which is what is then returned to the caller of _.reject()
@NewAtLearningThis As for how it plugs the function(value){} in for the filter - functions can be passed just like normal values in JS. Imagine calling a function such as add(1, 2). If add looked like function add(x, y){} then x would take the value of 1 and y would take the value of 2. The same thing is happening with filter(collection, test). The second value test takes the value of function(value){}, which you can then call within the filter method
Thank you Nick! This finally clicked for me thanks to your explanation. Makes perfect sense. I was trying to figure this out on my own for a few hours. Appreciate the help again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.