Search for text inside a nested object (for example, the Backbone.js collection)

I have a collection of backbone.js where I need to do a full search. The tools that I have are as follows:

Backbone.js, underscore.js, jQuery

For those who are not familiar with the spine:

A collection of basic objects is just an object. Inside the collection there is an array with models. Each model has an array with attributes. I have to look for every attribute for a string.

The code I use for this:

query = 'some user input'; query = $.trim(query); query = query.replace(/ /gi, '|'); var pattern = new RegExp(query, "i"); // this.collection.forEach is the same as _.each // only it get the models from the collection this.collection.forEach(function(model) { var check = true; _.each(model.attributes, function(attr){ if(pattern.test(attr) && check){ // Do something with the matched item check = false; } }, this); }, this); 

Maybe one of the tools I use has a better way to handle this?

+4
source share
2 answers

The trunk extends many underscores in the Collection class, so you can get rid of some of these things. Actually, you probably want to inject this into the collection itself as a method, then I would probably look at these keys using the old vintage for loop, especially if I wanted to break out of it.

 // in Backbone.Collection.extend search: function( query, callback ){ var pattern = new RegExp( $.trim( query ).replace( / /gi, '|' ), "i"); var collection = this; collection.each(function(model) { for( k in model.attributes ){ if( model.attributes.hasOwnProperty(k) && pattern.test(model.attributes[k]) ){ callback.call( collection, model, k ); break; // ends the for loop. } } }); } // later collection.search('foo', function( model, attr ){ console.log('found foo in '+model.cid+' attribute '+attr); }); 

However, this will only return the first match from the collection. You may prefer an implementation that returns an array of results as pairs of [model, attribute].

 // in Backbone.Collection.extend search: function( query, callback ){ var matches = []; var pattern = new RegExp( $.trim( query ).replace( / /gi, '|' ), "i"); this.each(function(model) { for( k in model.attributes ){ if( model.attributes.hasOwnProperty(k) && pattern.test(model.attributes[k]) ){ matches.push([model, k]); } } }); callback.call( this, matches ); } // later collection.search('foo', function( matches ){ _.each(matches, function(match){ console.log('found foo in '+match[0].cid+' attribute '+match[1]); }); }); 

Or, if you need an array of models that match, but don't care about which attribute matches, you can use filter

 // in Backbone.Collection.extend search: function( query, callback ){ var pattern = new RegExp( $.trim( query ).replace( / /gi, '|' ), "i"); callback.call( this, this.filter(function( model ){ for( k in model.attributes ){ if( model.attributes.hasOwnProperty(k) && pattern.test(k) ) return true; } })); } // later collection.search('foo', function( matches ){ _.each(matches, function(match){ console.log('found foo in '+match[0].cid+' somewhere'); }); }); 
+4
source

Your inner each blocks a short circuit, so you can switch to _.any() instead of _.each() and a combination of flags; any stops the iteration as soon as the callback function returns true , and also delegates its own some method, if available.

 this.collection.each(function(model) { _(model.attributes).any(function(attr, key) { if(!pattern.test(attr)) return false; // Do something with the matched item... return true; }); }); 

I also disabled context arguments this , since you are not using this anywhere, you can return them if they need to "Do something."

You can look at the output and reverse index of the collection if a simple regular expression search is not good enough.

+2
source

Source: https://habr.com/ru/post/1410942/


All Articles