views:

421

answers:

8

I need to display a formatted number on a web page using JavaScript. I want to format it so that there are commas in the right places. How would I do this with a regular expression? I've gotten as far as something like this:

myString = myString.replace(/^(\d{3})*$/g, "${1},");

...and then realized this would be more complex than I think (and the regex above is not even close to what I need). I've done some searching and I'm having a hard time finding something that works for this.

Basically, I want these results:

  • 45 becomes 45
  • 3856 becomes 3,856
  • 398868483992 becomes 398,868,483,992

...you get the idea.

A: 

Try something like this:

function add_commas(numStr)
{
    numStr += '';
    var x = numStr.split('.');
    var x1 = x[0];
    var x2 = x.length > 1 ? '.' + x[1] : '';
    var rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1)) {
        x1 = x1.replace(rgx, '$1' + ',' + '$2');
    }
    return x1 + x2;
}
Seth
A: 

I think you would necessarily have to do multiple passes to achieve this with regular expressions. Try the following:

  1. Run a regex for one digit followed by 3 digits.
  2. If that regex matches, replace it with the first digit, then a comma, then the next 3 digits.
  3. Repeat until (1) finds no matches.
Ryan Brunner
+1  A: 

First reverse a character array, then add commas after every third number unless it's just before the end of the string or before a - sign. Then reverse the character array again and make it a string again.

function add_commas(numStr){
    return numStr.split('').reverse().join('').replace(/(\d{3})(?=[^$|^-])/g, "$1,").split('').reverse().join('');
}
Harmen
You beat me to it...almost. See my answer for the part you missed.
Justin Johnson
+1  A: 

If you really want a regex, you can use two in a while loop:

while(num.match(/\d{4}/)) {
    num = num.replace(/(\d{3})(,\d|$)/, ',$1$2');
}

And if you want to be fancy, you can format numbers with decimal points too:

while(num.match(/\d{4}(\,|\.)/)) {
    num = num.replace(/(\d{3})(,\d|$|\.)/, ',$1$2');
}

Edit:

You can also do this with 2 regular expressions and no loop, splits, joins, etc:

num = num.replace(/(\d{1,2}?)((\d{3})+)$/, "$1,$2");
num = num.replace(/(\d{3})(?=\d)/g, "$1,");

The first regex puts a comma after the first 1 or 2 digits if the remaining number of digits is divisible by three. The second regex places a comma after every remaining group of 3 digits.

These won't work with decimals, but they work great for positive and negative integers.

Test output:

45
3,856
398,868,483,992

635
12,358,717,859,918,856
-1,388,488,184
Jeff B
A: 

Iteration isn't necessary

function formatNumber(n, separator) {
    separator = separator || ",";

    n = n.toString()
        .split("").reverse().join("")
        .replace(/(\d{3})/g, "$1" + separator)
        .split("").reverse().join("");

    // Strings that have a length that is a multiple of 3 will have a leading separator
    return n[0] == separator ? n.substr(1) : n;
}

var testCases = [1, 45, 2856, 398868483992];
for ( var i in testCases ) {
    if ( !ns.hasOwnProperty(i) ) { continue; }
    console.info(testCases[i]);   
    console.log(formatNumber(testCases[i]));
}

Results

1
1

45
45

2856
2,856

398868483992
398,868,483,992
Justin Johnson
+1  A: 

// You might want to take decimals into account

Number.prototype.commas= function(){
 var s= '', temp, 
 num= this.toString().split('.'), n=num[0];
 while(n.length> 3){
  temp= n.substring(n.length-3);
  s= ','+temp+s;
  n= n.slice(0, -3);
 }
 if(n) s= n+s;
 if(num[1]) s+='.'+num[1];
 return s;
}

var n= 10000000000.34;

n.commas() = returned value: (String) 10,000,000,000.34

kennebec
+7  A: 

This can be done in a single regex, no iteration required. If you weren't using JavaScript, you could simply use lookaround and just insert commas at the right places:

Search for (?<=\d)(?=(\d\d\d)+(?!\d)) and replace all with ,

But JavaScript doesn't support lookbehind, so that doesn't work. Fortunately, we only need to change a little bit:

Search for (\d)(?=(\d\d\d)+(?!\d)) and replace all with \1,

So, in JavaScript, that would look like:

result = subject.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");

Explanation: Assert that from the current position in the string onwards, it is possible to match digits in multiples of three, and that there is a digit left of the current position.

This will also work with decimals (123456.78) as long as there aren't too many digits "to the right of the dot" (otherwise you get 123,456.789,012).

Credit: Jeffrey Friedl, Mastering Regular Expressions, 3rd. edition, p. 66-67

Tim Pietzcker
Thanks! Exactly what I was looking for.
Brandon Montgomery
A: 

Someone mentioned that lookbehind isn't possible in Javascript RegExp. Here is a great page that explains how to use lookaround (lookahead and lookbehind).

http://www.regular-expressions.info/lookaround.html

Robusto