Underscore.js: how to chain custom functions
Solution 1
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.
Solution 2
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
Solution 3
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).
Related videos on Youtube
avernet
Co-founder of Orbeon, developing Orbeon Forms: web forms, open source, for the enterprise. Passionate about technology, and how it improves the world.
Updated on March 16, 2020Comments
-
avernet about 4 years
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 usetap()
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 oftap
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?-
Christian C. Salvadó over 13 yearsBe aware that
double
is a future reserved word and implementations may throw aSyntaxError
if it's used as an Identifier. -
Charlie Flowers over 13 yearsAlessandro, have you figured out yet that I gave you the only correct answer to this question? What I laid out does not pollute the underscore namespace at all. The underscore.js library anticipated what you asked for and built in a feature for exactly that.
-
CMircea over 9 yearsUnderscore now has both
tap
(runs a function to modify each item) andmap
(runs a function that returns a new item). -
avernet over 9 years@CMircea As of 2014-08-25, I don't see any indication that
tap()
can be used to modify the wrapped object. Am I missing something? underscorejs.org/#tap
-
-
avernet over 13 yearsyes, 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. -
avernet over 13 yearsI 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. -
Ibrahim Quraish over 13 years
apply
might be a better name -
avernet over 13 yearsright,
map
doesn't cut it as it only works on list. Here I need a sort-ofmap
that works on single "items". -
avernet over 13 years@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 ofobj.take(f, argArray)
. You don't think this could be confusion? (Honestly asking as I am not convinced myself either way.) -
Sasha Chedygov over 13 yearsI agree that this is way more ugly than the other solution you posted.
-
avernet over 13 yearsCharlie, yes, you could do this. I should have mentioned this in my question: I don't want to do it as it would pollute the
_
namespace. (Imagine if you have a large codebase using Underscore, and each time you want to do this you need to add the function to Underscore.) -
Charlie Flowers over 13 years@Alessandro Vernet - no it will not pollute the _ namespace. That is the beauty of it. When you call _([42,43]), it returns a brand new namespace just for you. When you then add "double" to it, your own namespace will have double, but _ itself will not.
-
Charlie Flowers over 12 years@Cesar Canassa - fair enough. That's why I said you can do something "like" this. I wasn't in front of my development machine with all the tools installed when I typed this. The point is that calling _ as a function returns a new object and won't pollute the "usual" _ namespace. Turns out, addToWrapper does not return this new object, so you have to add another line, as in: var obj = _([42, 43]); obj.addToWrapper(whatever); obj.chain().first().double().second(). And even that I can't certify as exactly right. The concepts are right, but I don't have a convenient place to check exact syntax.
-
Ishmael Smyrnow almost 11 yearsFor those looking,
tap
is now part of underscore core. -
arcseldon over 9 years@avernet you could simply prefix and postfix map with first.
-
saiyancoder about 3 yearsA little late to the party, but how about naming it
tapAndReturn
?