Seonixx
Seonixx

Reputation: 468

Passing additional arguments to function call (lodash)

Relating to the following mock code (_ relates to the lodash library):

var containerFunction = function () {
    var opts = {data: value};
    _.map(lines, functionForEach);
}

var functionForEach = function (line) {
  Do something for each line passed in from the maps function.
  Need to access opts in this scope.
  return value;
}

The argument line is received from the map function, but what would be the best way of passing the opts argument to the functionForEach function while keeping the rough pattern above (if possible).

I thought something like:

_.map(lines, functionForEach(opts))

or something similar might work but obviously doesn't.

Any suggestions?

Upvotes: 3

Views: 7439

Answers (3)

Adam Boduch
Adam Boduch

Reputation: 11211

Lodash has good utilities for function composition, including partially-applying arguments. However, this isn't actually so straightforward. Let's say we want to use partialRight():

function functionForEach(item, i) { return item * i; }

_.map([1, 2, 3], _.partialRight(functionForEach, 2));
// → [0, 2, 6]

Oops. That's not correct. The reason is that we're not getting the arguments passed to functionForEach() as we expect them. We want the i argument to always have a value of 2 and the item is the mapped collection value.

Let's try something else:

_.map([1, 2, 3], _.ary(_.partialRight(functionForEach, 2), 1));
// → [2, 4, 6]

That's better. The problem is that map() passes the item as the first argument, along with 2 other arguments (the index and the collection itself). Since we don't care about these latter two arguments, we can use ary() to compose a new function that ignores them.

Here's another approach:

_.map([1, 2, 3], _.partial(_.rearg(functionForEach, 1, 0), 2));
// → [2, 4, 6]

This time, we're using rearg() to change the order of our partial() function arguments. This approach I find to be less intuitive than simple going right for partialRight().

One final example:

_.map([1, 2, 3], _.flow(_.identity, _.partialRight(functionForEach, 2)));
// → [2, 4, 6]

Here, we're using the flow() higher-order function to compose our callback. This function takes the arguments supplied to it, and chains together the functions we pass to it - the output of the last function is the input to the next. We're using the identity() function here because it simply returns the first argument passed to it, which is exactly what we want.

This last approach is similar to the ary() approach - it's doing the same thing. The flow() approach is superior if we ever wanted to build on this behavior by passing in more functions, before or after functionForEach().

Upvotes: 5

Quy
Quy

Reputation: 1373

Take a look at _.partial. Docs:

partial

var containerFunction = function() {
  var opts = { data: value };
  _.map(lines, _.partial(functionForEach, opts));
};

var functionForEach = function (opts, line) { ... };

_.partial returns a new function with the arguments prepended to the functionForEach. In the example above, _.partial returns the following function signature:

function (line) { ... }

Calling the return function with a line calls functionForEach with line and opts defaulted to whatever you passed in as the argument in _.partial.

Upvotes: 3

user663031
user663031

Reputation:

You have three alternatives:

  1. Put functionForEach inside containerFunction. If you are not going to use functionForEach elsewhere, this makes the most sense.

  2. Write it as:

    var containerFunction = function () {
      var opts = {data: value};
      _.map(lines, function(elt) { return functionForEach(elt, opts); });
    }
    
    var functionForEach = function (line, opts) {
      Do something for each line passed in from the maps function.
      Need to access opts in this scope.
      return value;
    }  
    
  1. If you must, pass opts as the third (thisArg) argument to _.map and access it using this inside functionForEachvar.

Upvotes: 7

Related Questions