Tuesday, December 18, 2007

JavaScript and Currency

Ok. So I needed something for my first post. Though I am a huge fan and supporter of "pure" Java, a recent assignment had me looking how to validate the format for text entered in a currency field within a web page. So, almost as if my blog title were prophetic, my first post has to do with JavaScript.

As most of us do, I did a quick web search for how to do this. Most of the pages returned had to do with *adding* formatting to an already valid string. i.e. Turning 123456.78 into $123,456.78

As I read the various snippets of JavaScript, I began to formulate my own method for taking a string and converting it to a number. Quickly I decided to try and use Regular Expressions to help me. As I was narrowing down the syntax needed while continuing to hit websites, I found this helpful website which almost mirrored the expression I was working on. [I like the site's format, content, and interactive sections. I recommend you add it to your favorites.]

Here is the function that I came up with:
function parseMoneyDollars(input) {
var moneyxp = /^(\$)?(\d{1,3},?)*(\d{1,3})(\.\d{1,2})?/;
var money = 0;

if ( null == input ) {
} else {
if ( "" == input ) {
} else {
var result = input.match(moneyxp);
if ( result != null ) {
input = result[0].replace(/,/g,"").replace(/\$/g,"");
money = parseFloat(input);
} else {
confirm(input + " was not recognized as a US currency format.");
}
}
}

return money;
}

To my immediate recognition, the only difference between my RegEx and the one on the referenced website are:
  1. My expression is a little bit shorter because it groups by the thousands with the optional comma-delimiter. Some may argue that the following strings should not be recognized, but they are: 987,654321.98 7654,321.98
  2. Because my function converts and returns a number, there is no need to force the first group to the digits 1-9. Any leading zeroes will not "harm" the conversion.
I am currently finishing some testing of the code and regular expression. I will post an update soon. Thanks for visiting and welcome to my blog.

As suspected, my testing revealed several "undesired" results. For my needs, the following version makes the function better. The most significant difference is that it uses a different expression depending on whether the comma delimiter is detected. An important feature (but maybe less important for you) is the strict enforcement of the begin-string and end-string anchors:
function parseMoneyDollars(input) {
var moneyxp = /^(\$)?\d+(\.\d\d?)?$/;
var money = 0;

if ( null == input ) {
} else {
if ( "" == input ) {
} else {
if ( input.search(",") > -1 ) {
moneyxp = /^(\$)?(\d{1,3},)(\d{3},)*(\d{3})(\.\d{1,2})?$/;
}

var result = input.match(moneyxp);
if ( result != null ) {
input = result[0].replace(/,/g,"").replace(/\$/g,"");
money = parseFloat(input);
} else {
confirm(input + " was not recognized as a US currency format.");
}
}
}

return money;
}


Again, feedback is welcome and encouraged (but courtesy is expected even if you disagree with something ;-)