Wei Ma
Wei Ma

Reputation: 3155

underscorejs map with jquery

I am trying to use underscorejs contribe (http://documentcloud.github.io/underscore-contrib/) and jQuery to construct objects. My code looks like this

1.  var rightMap = _.rcurry2(_.map);
2.  var tabKeys = ['tab1', 'tab2', 'tab3', 'tab4'],
3.      rootElements = _.pipeline(
4.                         rightMap(function(k){return '#' + k;}),
5.                         rightMap(function(k){return $(k);})  //Here is what confused me
6.                     )(tabKeys);
//expected result: [$('#tab1'), $('#tab2'), $('#tab3'),$('#tab4')];

this code works as expected. However, I am not happy with line 5. I wanted to replace line 5 with

  rightmap($) 

this attempt broke the code. Only $('#tab1') was created. I was getting

  [$('#tab1'), [], [],[]];

as the result. I am wondering what is the differences between theses two pieces of code.

Thanks.

Update: I have added the link to the library I am using.

Update: add jsfiddle result http://jsfiddle.net/qbzuduom/1/ is the working situation http://jsfiddle.net/qbzuduom/2/ is the one with problem.

please press f12 in your browser and look at the console to see the difference.

What made the difference in the code?

Update: Thanks to the help from @muistooshort, I was able to generate the desired solution, and gained more understanding of the jquery $ function. Please check http://jsfiddle.net/qbzuduom/6/ for the updated demo

Upvotes: 1

Views: 58

Answers (1)

mu is too short
mu is too short

Reputation: 434685

I think your problem is that you misunderstand how _.rcurry2 works or how _.map works or possibly both.

From the fine _.rcurry2 manual:

Signature: _.rcurry2(func:Function)

Returns a curried version of fund where a maxium of two arguments are processed from right to left.

This, more or less, modifies how the arguments to _.map are handled so that instead of _.map(a, b) you can say rightMap(b)(a). Note that _.rcurry2 has nothing at all to do with how _.map calls its function.

Then for _.map we have:

map _.map(list, iteratee, [context])

Produces a new array of values by mapping each value in list through a transformation function (iteratee). If list is a JavaScript object, iteratee's arguments will be (value, key, list).

The _.map documentation leaves something very important out: it doesn't tell you what the iteratee arguments will be when list is an array, it only tells you want they'll be when list is an object. When you _.map an array, the iterator function is called as:

iteratee(list[i], i, list)

Take note of that second argument, that's where all your problems come from.

If we combine the above, we see that this:

rightMap($)

is the same as saying:

rightMap(function(k, i, list) { return $(k, i, list) })

The $ function does understand more than one argument so that i will be treated as a "context":

Internally, selector context is implemented with the `.find()` method, so  `$( "span", this )` is equivalent to  `$( this ).find( "span" )`.

and that gives us:

rightMap(function(k, i, list) { return $(i).find(k) })

which won't find anything useful. I'm guessing that jQuery is doing a simple if(context) check so the i === 0 case works because 0 is falsey.

In any case, you can't pass $ like that, you need to manually restrict the arguments it gets with something like:

var oneDollar = function() { $(arguments[0]) }

and rightMap(oneDollar) later on:

var rootElements = _.pipeline(
    rightMap(function(k) { return '#' + k }),
    rightMap(oneDollar)
)(tabKeys);

Demo: http://jsfiddle.net/ambiguous/f5uvaa4m/

The second argument to $ doesn't seem to be widely used these days so I'm not surprised that you missed it.

Upvotes: 1

Related Questions