views:

84

answers:

4

Using Underscore.js, I can write the following which returns 42:

_([42, 43]).chain()
    .first()
    .value()

I have custom function, not part of Underscore.js called double():

function double(value) { return value * 2; };

I would like to be able to call this function in an Underscore chain, as if it was part of Underscore. I would like to write the following, which I'd like to return 84:

_([42, 43]).chain()
    .first()
    .double()
    .value()

This can't work since Underscore doesn't define double(). I could use tap() as in:

_([42, 43]).chain()
    .first()
    .tap(double)
    .value()

This is valid, but tap applies the function to its argument and returns the argument, not the result of the function. So it looks to me like I would need a sort of tap that returns the result of the function applied to its argument. Is there anything like this in Underscore.js? Am I missing something terribly obvious?

+2  A: 

So you have a custom function:

function double(value) { return value * 2; }

You can use mixin to extend Underscore with it:

_.mixin({ double:double });

Now you can call your function from the Underscore object _:

_.double(42); // 84

and from the wrapped object returned from chain:

_([42, 43]).chain()
  .first()
  .double() // double made it onto the wrapped object too
  .value(); // 84
Roatin Marth
@Roatin, yes, that's one way of doing it. Something I don't like about this is that it pollutes `_`. I.e. this isn't workable if you use chaining all over the place in you code, and hence have a lot of those functions.
Alessandro Vernet
@Roatin, I suggested an alternative that requires me to add just one function to `_` - see the answer to my own question. I will experiment with this and see how well this works in practice.
Alessandro Vernet
+1  A: 

Not finding a tap that returns the value returns by the function is runs, I define one which I can take and add to _:

_.mixin({take: function(obj, interceptor) {
    return interceptor(obj);
}});

Then assuming I have:

function double(value) { return value * 2; };

I can write:

_([42, 43]).chain()
    .first()             // 42
    .take(double)        // Applies double to 42
    .value()             // 84

You can look at take as map on objects, instead of lists. Want to experiment with this? See this example on jsFiddle.

Alessandro Vernet
`apply` might be a better name
Gabe Moothart
@Gabe, I thought of calling it `apply`, but the name is already defined on functions, and it has a different semantic: `f.apply(obj, argArray)` instead of `obj.take(f, argArray)`. You don't think this could be confusion? (Honestly asking as I am not convinced myself either way.)
Alessandro Vernet
A: 

Does map work for this?

_([42, 43]).chain()
    .first()
    .map(double)
    .value()

edit

from the documentation, it looks like map would only work if you place it before the call to first:

_([42, 43]).chain()
    .map(double)
    .first()
    .value()
Gabe Moothart
@Gabe, right, `map` doesn't cut it as it only works on list. Here I need a sort-of `map` that works on single "items".
Alessandro Vernet
A: 

Using compose is another way dealing with the situation, but I think adding a function such as take as I suggested earlier is a better solution. Still, here is how the code would look like with compose:

function double(value) { return value * 2; };

_.compose(
    double,
    _.first,
    _.bind(_.identity, _, [42, 43])
)();

The initial value needs to be provided through a function which returns that value (here done by currying identity), and the functions need to be listed in an other which is the reverse of what you have with a chain, which appears as pretty unnatural to me.

Alessandro Vernet
I agree that this is way more ugly than the other solution you posted.
musicfreak