Reputation: 468
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
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
Reputation: 1373
Take a look at _.partial
. Docs:
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
Reputation:
You have three alternatives:
Put functionForEach
inside containerFunction
. If you are not going to use functionForEach
elsewhere, this makes the most sense.
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;
}
opts
as the third (thisArg
) argument to _.map
and access it using this
inside functionForEachvar
. Upvotes: 7