Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lodash.js partial application on function.apply

Given the following function, the usage of _.partial function throws an error:

function foo(x, y) { return 1 + 2; }
p = _.partial(foo.apply, null);
p([1,2]);

I get:

TypeError: Function.prototype.apply was called on [object Window], which is a object and not a function

What am I doing incorrectly here? Is there another way to achieve what I'm doing?

like image 390
CookieOfFortune Avatar asked Mar 12 '14 20:03

CookieOfFortune


3 Answers

i belive this what you're going for:

function foo(x, y) { return x + y; }
p = Function.apply.bind(foo, null);
p([1,2]); // ===3

the closest i can get underscore to do is via _.bind:

function foo(x, y) { return x + y; }
p = _.bind(foo.apply, foo, 0);
p([1,2]); // ===3

you may also want to consider another flexible use of that function, to sum a whole array of more than 2 elements:

 function foo(x, y) { return x + y; }
_.reduce([1,2,3,4,5], foo); // == 15

or using vanillaJS:

function foo(x, y) { return x + y; }
[1,2,3,4,5].reduce(foo); // == 15
like image 104
dandavis Avatar answered Nov 08 '22 23:11

dandavis


At the risk of misunderstanding the issue here, I wanted to share a tactic I just stumbled upon for a similar situation.

Problem:

The problem, in my case, is that sometimes I need to call a function (foo) with some arguments without changing the this context. Unfortunately, the list of arguments is not deterministic-- sometimes there might be 2, sometimes there might be 3, etc. And since _.partial expects each argument to be passed in as a separate argument, I was initially stumped. For example:

// some dynamic array of args
var args = [new Error(), [{id: 3}, {id: 7}], function cb() {}];

// the function we want the partially applied to
var fn = function foo ( /* ...want `args` in here... */ ) {
  // ..implementation...
}

// can't do it with normal _.partial() usage...
// var newFn = _.partial(fn, args[0], args[1], ... args[args.length])

Solution:

This is much the same use case when you'd want to use apply instead of call to invoke a function normally. So after putzing around w/ it for a while, I realized I could just do:

var argsToLodashPartialFn = [foo].concat(args);
var newFn = _.partial.apply(null, argsToLodashPartialFn);

tldr;

Or, more concisely:

var newFn = partialApply(foo, args);

// === newFn(args[0], args[1], ...., args[n])
newFn();

And here's the implementation as a reusable function:

/**
 * partialApply()
 *
 * Like `_.partial(fn, arg0, arg1, ..., argN)`,
 * but for a dynamic array of arguments:
 *
 * @param {Function} fn
 * @param {Array}    args
 * @return {Function}
 */

function partialApply (fn, args) {
  return _.partial.apply(null, [fn].concat(args));
}
like image 39
mikermcneil Avatar answered Nov 09 '22 01:11

mikermcneil


As of lodash 3.2, you would use the spread() function for this:

function foo(x, y) { return x + y; }
var p = _.spread(foo);
p([ 1, 2 ]); // → 3
like image 1
Adam Boduch Avatar answered Nov 09 '22 00:11

Adam Boduch