View Full Version : simple list filtering

rnd me
11-10-2011, 05:34 PM
Filtering lists is usually done by hand with some setup involving a valid item collector, a for loop and one or more conditionals.
That works, but is quite a bit to type out, and i've discovered a very simple way to do so using native javascript statements. I noticed it re-reading the spec last month, and it's already saved me a lot of code writing on a data-centric web server backend, so i thought i'd share the trick here.

the secret is using the generic potential of RegExp's test() method to filter the results. Since RegExp().test(strData) returns true or false, it's perfect for feeding Array.filter().

code examples (live demo (http://danml.com/sandbox/#//%20Click%20%22Run%22%20to%20test...%0A%0Avar%20text=%22WITHIN%20a%20few%20minutes%20the%20news%20had %20spread,%20and%20a%20dozen%20skiff-loads%20of%20men%20were%20on%20their%20way%20to%20McDougal%27s%20cave,%20and%20the%20ferryboat,%20we ll%20filled%20with%20passengers,%20soon%20followed.%20Tom%20Sawyer%20was%20in%20the%20skiff%20that%2 0bore%20Judge%20Thatcher.%22%0A%0A%0A//example%20array:%20an%20array%20with%20each%20slot%20holding%20one%20word%20from%20above%20:%0Avar%2 0words=text.split%28/[\s,.]+/%29;%0A%0A%0Aalert%28%22All%20words%20\n%22+%20%0A%0A%20%20words.join%28%22\n%22%29%0A%29;%0A%0A%0A% 0A%0Aalert%28%22Words%20that%20are%205%20or%20more%20chars%20in%20length%20\n%22+%20%0A%0A%20%20word s.filter%28%20RegExp%28%29.test,%20%20/\w{5,}/%20%29.join%28%22\n%22%29%0A%29;%0A%0A%0A%0A%0Aalert%28%22Words%20starting%20with%20a%20vowel%20\n%2 2+%20%0A%0A%20%20words.filter%28%20RegExp%28%29.test,%20%20/^[aeiou]/i%20%29.join%28%22\n%22%29%0A%29;)):

//example text data (from Tom Sawyer) :
var text="WITHIN a few minutes the news had spread, and a dozen skiff-loads of men were on their way to McDougal's cave, and the ferryboat, well filled with passengers, soon followed. Tom Sawyer was in the skiff that bore Judge Thatcher."

//example array: an array with each slot holding one word from above :
var words=text.split(/[\s,.]+/);

alert("All words \n"+


alert("Words that are 5 or more letters long \n"+

words.filter( RegExp().test, /\w{5,}/ ).join("\n")

alert("Words starting with a vowel \n"+

words.filter( RegExp().test, /^[aeiou]/i ).join("\n")

boring details:
A minor technical glitch is that Array.filter separates the method from the context, which means that a .test method on a RegExp instance points to the wrong "this" when executed. That's over-come by using a duck-type compatible this object: the actual RegExp you want to you to test against to set this. Array.filter takes a 2nd argument: this. In short, it takes 2 RegExps to get this to work, but it's still very quick.

If you make a RegExp ahead of time, you don't have the cool one-liner aspect of the routine anymore, but you can simplify/shorten the call somewhat:

var rx=/e/;
words.filter( rx.test, tx );

[].filter() and [].map() are powerful, and fast and simple when used with native methods. you may have noticed the empty array slot at the end of words. from the "all words" demo. If that's a problem, filter it out with another native:

var words=text.split(/[\s,.]+/).filter(String);

Boolean and String are both great [].filter() arguments

Number is great for strong-typing numerical arrays with map(), or removing non-numbers with filter():

"1,2,3,4,5".split(",").map(Number); //==[1,2,3,4,5]
if you need to support older IE versions, you can makes sure [].map is available with code from MDN (https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map#Compatibility), use the underscore lib (http://documentcloud.github.com/underscore/), http://danml.com/f/, or many other libraries.

here is [].map, [].filter, [].reduce, and all the other "Array 1.6" methods in under 1kb:

eval(function (s,r){var a,x,i=0,m=r.length,q=/([.*+?^${}()|[\]\/\\])/g;for(;i<m;i++){a=r[i]||"";x=RegExp(a.slice(0,1).replace(q,"\\$1"),"g");s=s.replace(x,a.slice(1))}return s}("(&n(#var o=Array.prototype,H,i,e={map/i=0,r=[]BVr[i]8G,V}Nr9J<0Bt@a.3(G,V#r[g++Kt[i]MNr9every/i=0;Nm@t.JU.length==m9some/i=1;Q(;mT;#P@a.3(tDmCm,V@!Ti#$!0}}$!19lastIndexOf/i=b6-1;Q(;m>i;mT#P@t[mK==a#Nl}}$-194dexOf/i=b60Bt@t[iK==a#Ni}}$-19reduce/i=0,r=b6t[i++]L57r8nul*reduceRight/i=m-1,r=b6t[iT]%>-1;iT#r8nul*QEach:&n(a,b#this.concat().mapU9clean<0,x,O;a=Function(a6Str4g)Bt@(x8G,V)#r[g++KxMNarguments[2]?b:rMQ(H 4 e#i=o[H];o[HKi6e[H]}}());","Vt)`U(a,b)`T--`BT4 `TLB `Pif(m 4 t`N$ `M}};`</i=0,r=[Cg=`L%<`K]=`Jfilter`Hit`GbDiCi`D,t[`C],`6||`B576`Qfor`&functio`@&&`9},`8=a.3(`7++#`6if(i`5m;i`4in`3call`#){`/:#,`*l,r,t[i],i,t)}$ r},`%;for(;i`$return`#functio$length`$n(a,b){var t=#=t.`#this.concat(),m".split('`')));