Applying Masks Using Regular Expressions In Javascript

Posted August 1, 2006 at 8:43 AM

Tags: Javascript / DHTML

I have blogged several times about using regular expressions (RegExp) in Javascript. I think regular expressions are absolutely wonderful. And, one of the great things about Javascript regular expressions (as opposed to reg ex in other languages) is that you can pass the individual groups matches to a function call:

 Launch code in new window » Download code as text file »

  • strValue.replace(
  • new RegExp( "([0-9]+)", "gi" ),
  •  
  • // Pass a function as the "replace" argument for
  • // the regular expression.
  • function( $1 ){
  • // Return the value you want to replace into
  • // the string.
  • return( "digit" );
  • });

This is a really cool feature, but it was not until recently that I came to see the amazing POWER of this feature. I am working on a prototype library of useful functions such as string trimming and date formatting. Date and time formatting, as you might know from ColdFusion, takes a mask argument to define how the date should be formatted. For example, the mask "mm/dd/yyyy" would return the string "08/01/2006".

There are many different combinations of masks available to the user. As it turns out, using this regular expression flexibility, it actually makes applying a mask extremely easy. To explore this, let's take a look at my formatDate() function:

 Launch code in new window » Download code as text file »

  • (new Date()).formatDate( "mmm d, yyyy" );

The first thing I do inside the formatDate() function is define the values for each part of the potential mask:

 Launch code in new window » Download code as text file »

  • // Create the values for each part of the potential date mask.
  • var objParts = {
  • "d": this.getDate(),
  • "dd": (this.getDate().toString().length == 1) ? ("0" + this.getDate()) : this.getDate(),
  • "ddd": [ "Sun","Mon","Tue","Wed","Thr","Fri","Sat" ][ this.getDay() ],
  • "dddd": [ "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" ][ this.getDay() ],
  • "m": this.getMonth(),
  • "mm": (this.getMonth().toString().length == 1) ? ("0" + this.getMonth()) : this.getMonth(),
  • "mmm": [ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" ][ this.getMonth() + 1 ],
  • "mmmm": [ "January","February","March","April","May","June","July","August","September","October","November","December" ][ this.getMonth() + 1 ],
  • "yy": this.getYear().toString().substring( 1, 3 ),
  • "yyyy": this.getFullYear()
  • }

Each part of the object (equivalent to a ColdFusion struct), objPart, is a possible part of the date mask. Now, assuming that the passed argument "strMask" holds the user's desired date format, we could apply the mask to the date using regular expression:

 Launch code in new window » Download code as text file »

  • // There was no special date formatting, so just use the mask.
  • return(
  • strMask.replace(
  • // Set up the regular expression for possible mask parts.
  • new RegExp( "(d{1,4}|m{1,4}|y{4}|y{2})", "gi" ),
  •  
  • // Have each match get passed to this function.
  • function( $1 ){
  • // Return the equivalent mask part as it applies to the
  • // date object.
  • return( objParts[ $1 ] );
  • })
  • );

As you can see, the regular expression (d{1,4}|m{1,4}|y{4}|y{2}) will simply match any possible part of the available date mask options. Each matching part is then passed to the function we defined as the second argument to the replace() method. This function merely returns the value of the objParts[] object using the matched group as the key. Take a second to sit back and be in awe of the amazing flexibility that this allows you.

Putting it all together:

 Launch code in new window » Download code as text file »

  • // Formats the date with the given date mask. The mask is returned
  • // and the internal date is not altered.
  • Date.prototype.formatDate = function( strMask ){
  • // Create the values for each part of the potential date mask.
  • var objParts = {
  • "d": this.getDate(),
  • "dd": (this.getDate().toString().length == 1) ? ("0" + this.getDate()) : this.getDate(),
  • "ddd": [ "Sun","Mon","Tue","Wed","Thr","Fri","Sat" ][ this.getDay() ],
  • "dddd": [ "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" ][ this.getDay() ],
  • "m": this.getMonth(),
  • "mm": (this.getMonth().toString().length == 1) ? ("0" + this.getMonth()) : this.getMonth(),
  • "mmm": [ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" ][ this.getMonth() + 1 ],
  • "mmmm": [ "January","February","March","April","May","June","July","August","September","October","November","December" ][ this.getMonth() + 1 ],
  • "yy": this.getYear().toString().substring( 1, 3 ),
  • "yyyy": this.getFullYear()
  • }
  •  
  • // Check to see if we have special date formatting options.
  • switch ( strMask ){
  • case "short":
  • return( objParts[ "m" ] + "/" + objParts[ "d" ] + "/" + objParts[ "yyyy" ] );
  • break;
  •  
  • case "medium":
  • return( objParts[ "mmm" ] + " " + objParts[ "d" ] + ", " + objParts[ "yyyy" ] );
  • break;
  •  
  • case "long":
  • return( objParts[ "mmmm" ] + " " + objParts[ "d" ] + ", " + objParts[ "yyyy" ] );
  • break;
  •  
  • case "full":
  • return( objParts[ "dddd" ] + ", " + objParts[ "mmmm" ] + " " + objParts[ "d" ] + ", " + objParts[ "yyyy" ] );
  • break;
  •  
  • default:
  • // There was no special date formatting, so just use the mask.
  • return(
  • strMask.replace(
  • new RegExp( "(d{1,4}|m{1,4}|y{4}|y{2})", "gi" ),
  • function( $1 ){
  • return( objParts[ $1 ] );
  • })
  • );
  • break;
  • }
  • }

Can you imagine trying to do this without regular expressions? Let's take a look at an example that doesn't use regular expression. This example was taken off of the Javascript Tool Box:

 Launch code in new window » Download code as text file »

  • function formatDate(date,format){format=format+"";var result="";var i_format=0;var c="";var token="";var y=date.getYear()+"";var M=date.getMonth()+1;var d=date.getDate();var E=date.getDay();var H=date.getHours();var m=date.getMinutes();var s=date.getSeconds();var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;var value=new Object();if(y.length < 4){y=""+(y-0+1900);}value["y"]=""+y;value["yyyy"]=y;value["yy"]=y.substring(2,4);value["M"]=M;value["MM"]=LZ(M);value["MMM"]=MONTH_NAMES[M-1];value["NNN"]=MONTH_NAMES[M+11];value["d"]=d;value["dd"]=LZ(d);value["E"]=DAY_NAMES[E+7];value["EE"]=DAY_NAMES[E];value["H"]=H;value["HH"]=LZ(H);if(H==0){value["h"]=12;}else if(H>12){value["h"]=H-12;}else{value["h"]=H;}value["hh"]=LZ(value["h"]);if(H>11){value["K"]=H-12;}else{value["K"]=H;}value["k"]=H+1;value["KK"]=LZ(value["K"]);value["kk"]=LZ(value["k"]);if(H > 11){value["a"]="PM";}else{value["a"]="AM";}value["m"]=m;value["mm"]=LZ(m);value["s"]=s;value["ss"]=LZ(s);while(i_format < format.length){c=format.charAt(i_format);token="";while((format.charAt(i_format)==c) &&(i_format < format.length)){token += format.charAt(i_format++);}if(value[token] != null){result=result + value[token];}else{result=result + token;}}return result;}

While the code is hard to read (I wasn't about to take the time to parse it), you can clearly see that there are multiple WHILE loops. And within each of those while loops they are getting tokens, performing logic, checking for null values. All a complete waste of time. Let Javascript perform the heavy lifting for you.

Are you beginning to see the possibilities? Regular expressions are so freakin' powerful it almost makes my ears bleed to think about it.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Other Searches  |  Print Page





Reader Comments

Sep 8, 2006 at 1:54 PM // reply »
1 Comments

This looks pretty nice, but do you have an example of implementation. I'm not familiar with this particular syntax (relatively new to js). How do you call this exactly?


Sep 8, 2006 at 2:26 PM // reply »
7,572 Comments

Dylan,

I have thrown together a little demo of how this works, take a look, especially at the source code. It's a bit complicated. It basically takes the date object and prototypes two format functions, dateFormat() and timeFormat().

http://www.bennadel.com/resources/demo/2/


Jan 31, 2007 at 10:33 PM // reply »
165 Comments

Ben, this rocks! Thanks for sharing.


Feb 1, 2007 at 7:57 AM // reply »
7,572 Comments

No problem dude. Please let me know if you run into any Javascript road blocks that you need help with. I love me some Javascript.


Jul 17, 2007 at 6:40 PM // reply »
1 Comments

The indexes into the month names and the number of the month look to be flipped around.

I think the lines

# "m": this.getMonth(),
# "mm": (this.getMonth().toString().length == 1) ? ("0" + this.getMonth()) : this.getMonth(),
# "mmm": [ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" ][ this.getMonth() + 1 ],
# "mmmm": [ "January","February","March","April","May","June","July","August","September","October","November","December" ][ this.getMonth() + 1 ],

should be

# "m": this.getMonth() + 1,
# "mm": (this.getMonth().toString().length == 1) ? ("0" + this.getMonth()) : this.getMonth() + 1,
# "mmm": [ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" ][ this.getMonth() ],
# "mmmm": [ "January","February","March","April","May","June","July","August","September","October","November","December" ][ this.getMonth() ],


Jul 18, 2007 at 7:28 AM // reply »
7,572 Comments

It is possible. I go back and forth between ColdFusion (1-based arrays) and Javascript (0-based arrays) that I may have just messed it up. However, I am pretty sure I tested this stuff, but it's possible that I was so excited about the regular expressions that I didn't notice it was the wrong month :)


Post Comment  |  Ask Ben

Recent Blog Comments
Mar 21, 2010 at 11:13 AM
A New Wrist Pain
@chiropractor suwanee, Spoken like someone trying to sell something. Other than for minor, temporary relief from some back pain, chiropractic treatment is nothing but placebo effect and quackery. ... read »
Mar 21, 2010 at 6:32 AM
ColdFusion CFPOP - My First Look
Apologies... The field name in the db for C. is "BounceCode" It stores the code / message which is returned in the email. Sorry for the confusion. ... read »
Mar 21, 2010 at 6:29 AM
ColdFusion CFPOP - My First Look
@Jose Galdamez, Hi Ben and Jose 1st of all.. big thanks to Jose for his Skype chat a few weeks back. Your time was much appreciated. I have come up with a rather unelegant solution to my problem a ... read »
Mar 21, 2010 at 3:42 AM
A New Wrist Pain
Chiropractic treatment is one of the best methods for treating numerous health problems naturally. After years of experience being a chiropractor, I have found that it is a powerful way to solve many ... read »
Mar 20, 2010 at 12:07 PM
Drawing On The iPhone Canvas With jQuery And ColdFusion
Simply awesome. Saved my day. ... read »
Mar 20, 2010 at 9:00 AM
Building A Fixed-Position Bottom Menu Bar (ala FaceBook)
I would like to say thx for an easy way to create a bottom bar. I do have a ?. Is it possible to center the bar if i want to resize it to ex 85%. Regards Offenbach ... read »
Mar 19, 2010 at 7:26 PM
MySQL 3/4 - com.mysql.jdbc.Driver And allowMultiQueries=true
Thank you very much for this post. Adding allowMultiQueries="true" in context.xml didn't help until I added it to url as allowMultiQueries=true Good idea is to use prepared statements and it will he ... read »
Jim
Mar 19, 2010 at 4:49 PM
Nobody Puts Baby In The Corner!
Wow. This is like suddenly finding a support group for your secret shame. I'm not alone! I always liked this movie, even though it is extremely cheesy. I just wish Jennifer Grey hadn't gotten the ... read »