开发者

Underscore.js: how to chain custom functions

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 dou开发者_开发知识库ble(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?


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.


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


Alright, I'm fresh off of reading the underscore annotated source code for the first time. But I think you can do something like this:

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

var obj = _([42, 43]).addToWrapper({double:double});

obj.chain()
  .first()
  .double()
  .value();

The syntax/details might not be right, but the core point is this: when you call _([42,43]), you're calling underscore as a function. When you do so, it instantiates a new object and then mixes into that object most of the underscore functions. Then, it returns that object to you. You can then add your own functions to that object, and none of this pollutes the "_" namespace itself.

That's what the underscore.js code looked like to me. If I'm wrong, I'd like to find out and hopefully someone will explain why.

EDIT: I've actually been using underscore.js heavily for about a month now, and I have gotten pretty familiar with it. I now know it behaves like I said here. When you call _ as a Constructor function, you get back your own "namespace" (just an object), and you can add things to it with addToWrapper() that show up in your namespace but not in the "global" "_" namespace. So the feature the OP wanted is already built in. (And I have been really impressed with underscore, btw, it is very very nicely done).


Looks like lodash has implemented exactly what you are looking for:

_.thru(value, interceptor)

from the docs:

This method is like _.tap except that it returns the result of interceptor

https://lodash.com/docs#thru


Many ways to easily achieve this, here is one such solution:

  _.chain([42,43])
    .first(1)
    .map(double)
    .first()
    .value();
    // 84

However, I would recommend using Ramda then with auto-curry and pipe / compose these kinds of tasks are trivial:

R.pipe(R.head, R.multiply(2))([42, 43]);  // 84

equivalent to:

R.compose(R.multiply(2), R.head)([42, 43]);  // 84

If you wanted to extend the solution to take say the first 2 items, instead of a single value, as requested by the OP, then:

R.pipe(R.take(2), R.map(R.multiply(2)))([42, 43])  // [84, 86]

However, in Ramda R.compose is preferred. Either way, for trivial tasks like this, it does tend to be more convenient and easy to use.


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()


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜