3

I saw this beautiful script to add thousands separator to js numbers:

function thousandSeparator(n, sep)
{
    var sRegExp = new RegExp('(-?[0-9]+)([0-9]{3})'),
        sValue = n + '';
    if(sep === undefined)
    {
        sep = ',';
    }
    while(sRegExp.test(sValue))
    {
        sValue = sValue.replace(sRegExp, '$1' + sep + '$2');
    }
    return sValue;
}

usage :

thousandSeparator(5000000.125, '\,') //"5,000,000.125"

However I'm having a trouble accepting the while loop.

I was thinking to change the regex to : '(-?[0-9]+)([0-9]{3})*' asterisk...

but now , how can I apply the replace statement ?

now I will have $1 and $2..$n

how can I enhance the replace func?

p.s. the code is taken from here http://www.grumelo.com/2009/04/06/thousand-separator-in-javascript/

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • 1
    Btw, it fails on `5000000.125678` -> `5,000,000.125,678` – zerkms May 16 '12 at 05:05
  • @zerkms yep you right. http://www.grumelo.com/2009/04/06/thousand-separator-in-javascript/ – Royi Namir May 16 '12 at 05:06
  • Check out [this question](http://stackoverflow.com/questions/3753483/javascript-thousand-separator-string-format) - there's a link [to an example](http://www.mredkj.com/javascript/numberFormat.html#addcommas) of how to do what you're trying. – Rob I May 16 '12 at 05:07
  • @RobI again , it still has a while loop. i think a better solution can be found....( or not) ...:) – Royi Namir May 16 '12 at 05:09
  • @Cylian i think we need positive look ahead `(-?[0-9]+)([0-9]{3})(?=\.)` but it not working... i mean - only replace numbers which in their right theres a DOT. – Royi Namir May 16 '12 at 05:18
  • @zerkms how can i make this work only on the left side ? – Royi Namir May 16 '12 at 05:24

5 Answers5

8

There is no need to use replace, you can just add toLocaleString instead:

console.log((5000000.125).toLocaleString('en'));

More information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString

andyrandy
  • 72,880
  • 8
  • 113
  • 130
  • Thanks . really nice. I've would've select this answer unless i've written [regex] in tags – Royi Namir Apr 09 '18 at 10:07
  • the other guy put a lot of effort into his answer, so he deserves the accept anyway ;) - but you really should not use regex for this, the native way is better (and easier to understand). – andyrandy Apr 09 '18 at 10:16
  • 1
    You can be sure I'll use it from now :) – Royi Namir Apr 09 '18 at 10:22
  • @luschen I feel like such a noob for not knowing this one. I feel like I've written regex so many times to do this, but based on me being on this post I also always clearly screw it up every-time and have to google it again. This one I might not forget though ... plus localisation, yay! – cfkane Apr 15 '21 at 07:40
5

Your assumption

now i will have $1 and $2..$n

is wrong. You have two groups, because you have two sets of brackets.

    (-?[0-9]+)([0-9]{3})*
1.  ^^^^^^^^^^
2.            ^^^^^^^^^^

And then you repeat the second group. If it matches the second time, it overwrites the result of the first match, when it matches the third time, it overwrites ...

That means when matching is complete, $2 contains the value of the last match of that group.

First approach

(\d)(?=(?:[0-9]{3})+\b)

and replace with

$1,

See it on Regexr

It has the flaw that it does insert the comma also on the right of the dot. (I am working on it.)

Second approach

(\d)(?:(?=\d+(?=[^\d.]))(?=(?:[0-9]{3})+\b)|(?=\d+(?=\.))(?=(?:[0-9]{3})+(?=\.)))

and replace with

$1,

See it on Regexr

So now its getting a bit more complicated.

(\d)                   # Match a digit (will be reinserted)
(?:
    (?=\d+(?=[^\d.]))  # Use this alternative if there is no fractional part in the digit
    (?=(?:\d{3})+      # Check that there are always multiples of 3 digits ahead
    \b)                # Till a word boundary
    |                  # OR
    (?=\d+(?=\.))      # There is a fractional part
    (?=(?:\d{3})+      # Check that there are always multiples of 3 digits ahead
    (?=\.))            # Till a dot
)

Problem: does also match the fractional part if there is not the end of the string following.

stema
  • 90,351
  • 20
  • 107
  • 135
  • hi stema , yeah you right. but as you can see im facing 2 problems. it does considers numebers from the right 123.1234567 and i need to get rid of the while loop – Royi Namir May 16 '12 at 05:47
  • @RoyiNamir I extended my answer, but so far only for the second problem. The first is still a problem. – stema May 16 '12 at 05:55
  • @stema looking good, if you can fix up that part to the right I just lost a bet with myself. – Dagg Nabbit May 16 '12 at 05:56
  • @ggg you will own yourself 5 $ – Royi Namir May 16 '12 at 06:00
  • @RoyiNamir I think I found a solution, please test it. – stema May 16 '12 at 06:11
  • @RoyiNamir, it works only if there is the end of the string after the fractional part. At the moment I don't know a solution, JavaScript Regex is too limited. I am not sure how you want to use it, if you only give the number and it ends with the end of string, this should work. – stema May 16 '12 at 06:49
1

Here is an ugly script to contrast your beautiful script.

10000000.0001 .toString().split('').reverse().join('')
.replace(/(\d{3}(?!.*\.|$))/g, '$1,').split('').reverse().join('')

Since we don't have lookbehinds, we can cheat by reversing the string and using lookaheads instead.

Here it is again in a more palatable form.

function thousandSeparator(n, sep) {

    function reverse(text) {
        return text.split('').reverse().join('');
    }

    var rx = /(\d{3}(?!.*\.|$))/g;

    if (!sep) {
        sep = ',';
    }

    return reverse(reverse(n.toString()).replace(rx, '$1' + sep));

}
Dagg Nabbit
  • 75,346
  • 19
  • 113
  • 141
  • do i need to download winrar to use this code ? lol ( very long)...:) thanks – Royi Namir May 16 '12 at 05:19
  • @RoyiNamir what do you think of the cleaned up version? Reversing the string not doing it for you? – Dagg Nabbit May 16 '12 at 05:35
  • ive tested it in jsperf and its very slow ( compare to the main sample - which is not working with commas at the right..) i need to fix my solution so it first : get only numbers from the right , second : remove the while loop. – Royi Namir May 16 '12 at 05:36
  • 1
    @RoyiNamir well this does fix both of those things, but I don't doubt that it's slow to reverse the string like this... I think lookarounds may be slow as well. Curious to see what others come up with. – Dagg Nabbit May 16 '12 at 05:38
  • i think we need to do the first phase first... how to make it work only on the left numbers... – Royi Namir May 16 '12 at 05:41
1

How about this one:

result = "1235423.125".replace(/\B(?=(\d{3})+(?!\d))/g, ',') //1,235,423.125
redOctober13
  • 3,662
  • 6
  • 34
  • 61
0

Try this one:

result = subject.replace(/([0-9]+?)([0-9]{3})(?=.*?\.|$)/mg, "$1,$2");

Test here

Cylian
  • 10,970
  • 4
  • 42
  • 55