bento
bento

Reputation: 5026

underscore.js filter function

I'm attempting to learn backbone.js and (by extension) underscore.js, and I'm having some difficulty understanding some of the conventions. While writing a simpel search filter, I thought that something like below would work:

var search_string = new RegExp(query, "i");

        var results = _.filter(this, function(data){
            return search_string.test(data.get("title"));
        }));

But, in fact, for this to work I need to change my filter function to the following:

var search_string = new RegExp(query, "i");

        var results = _(this.filter(function(data){
            return search_string.test(data.get("title"));
        }));

Basically, I want to understand why the second example works, while the first doesn't. Based on the documentation (http://documentcloud.github.com/underscore/#filter) I thought that the former would have worked. Or maybe this just reflects some old jQuery habits of mine... Can anyone explain this for me?

Upvotes: 3

Views: 5769

Answers (2)

mu is too short
mu is too short

Reputation: 434685

I'd guess that you're using a browser with a native Array#filter implementation. Try these in your console and see what happens:

[].filter.call({ a: 'b' }, function(x) { console.log(x) });
[].filter.call([1, 2],     function(x) { console.log(x) });

The first one won't do anything, the second will produce 1 and 2 as output (http://jsfiddle.net/ambiguous/tkRQ3/). The problem isn't that data is empty, the problem is that the native Array#filter doesn't know what to do when applied to non-Array object.

All of Underscore's methods (including filter) use the native implementations if available:

Delegates to the native filter method, if it exists.

So the Array-ish Underscore methods generally won't work as _.m(collection, ...) unless you're using a browser that doesn't provide native implementations.

A Backbone collection is a wrapper for an array of models, the models array is in c.models so you'd want to:

_.filter(this.models, function(data) { ... });

Backbone collections have several Underscore methods mixed in:

Backbone proxies to Underscore.js to provide 28 iteration functions on Backbone.Collection.

and one of those is filter. These proxies apply the Underscore method to the collection's model array so c.filter(...) is the same as _.filter(c.models, ...).

This mixing-in is probably what's confusing the "should I use the native method" checks that Underscore is doing:

if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);

You can use _.filter on a plain old object (_.filter({a:'b'}, ...)) and get sensible results but it fails when you _.filter(backbone_collection, ...) because collections already have Underscore methods.

Here's a simple demo to hopefully clarify things: http://jsfiddle.net/ambiguous/FHd3Y/1/

Upvotes: 4

mccow002
mccow002

Reputation: 6914

For the same reason that $('#element') works and $#element doesn't. _ is the global variable for the underscore object just like $ is the global variable for the jQuery object.

_() says look in the _ object. _filter says look for a method named _filter.

Upvotes: -1

Related Questions