NewAtLearningThis
NewAtLearningThis

Reputation: 239

Underscore.js function within a function

  _.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

Upvotes: 0

Views: 154

Answers (2)

Nick Parsons
Nick Parsons

Reputation: 50684

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.

Upvotes: 1

Spidy
Spidy

Reputation: 40002

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

Upvotes: 1

Related Questions