How to print a number with commas as thousands separators in JavaScript

1,699,246

Solution 1

I used the idea from Kerry's answer, but simplified it since I was just looking for something simple for my specific purpose. Here is what I have:

function numberWithCommas(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

function numberWithCommas(x) {
    return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}

function test(x, expect) {
    const result = numberWithCommas(x);
    const pass = result === expect;
    console.log(`${pass ? "✓" : "ERROR ====>"} ${x} => ${result}`);
    return pass;
}

let failures = 0;
failures += !test(0,        "0");
failures += !test(100,      "100");
failures += !test(1000,     "1,000");
failures += !test(10000,    "10,000");
failures += !test(100000,   "100,000");
failures += !test(1000000,  "1,000,000");
failures += !test(10000000, "10,000,000");
if (failures) {
    console.log(`${failures} test(s) failed`);
} else {
    console.log("All tests passed");
}
.as-console-wrapper {
    max-height: 100% !important;
}

The regex uses 2 lookahead assertions:

  • a positive one to look for any point in the string that has a multiple of 3 digits in a row after it,
  • a negative assertion to make sure that point only has exactly a multiple of 3 digits. The replacement expression puts a comma there.

For example, if you pass it 123456789.01, the positive assertion will match every spot to the left of the 7 (since 789 is a multiple of 3 digits, 678 is a multiple of 3 digits, 567, etc.). The negative assertion checks that the multiple of 3 digits does not have any digits after it. 789 has a period after it so it is exactly a multiple of 3 digits, so a comma goes there. 678 is a multiple of 3 digits but it has a 9 after it, so those 3 digits are part of a group of 4, and a comma does not go there. Similarly for 567. 456789 is 6 digits, which is a multiple of 3, so a comma goes before that. 345678 is a multiple of 3, but it has a 9 after it, so no comma goes there. And so on. The \B keeps the regex from putting a comma at the beginning of the string.

@neu-rah mentioned that this function adds commas in undesirable places if there are more than 3 digits after the decimal point. If this is a problem, you can use this function:

function numberWithCommas(x) {
    var parts = x.toString().split(".");
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    return parts.join(".");
}

function numberWithCommas(x) {
    var parts = x.toString().split(".");
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    return parts.join(".");
}

function test(x, expect) {
    const result = numberWithCommas(x);
    const pass = result === expect;
    console.log(`${pass ? "✓" : "ERROR ====>"} ${x} => ${result}`);
    return pass;
}

let failures = 0;
failures += !test(0              , "0");
failures += !test(0.123456       , "0.123456");
failures += !test(100            , "100");
failures += !test(100.123456     , "100.123456");
failures += !test(1000           , "1,000");
failures += !test(1000.123456    , "1,000.123456");
failures += !test(10000          , "10,000");
failures += !test(10000.123456   , "10,000.123456");
failures += !test(100000         , "100,000");
failures += !test(100000.123456  , "100,000.123456");
failures += !test(1000000        , "1,000,000");
failures += !test(1000000.123456 , "1,000,000.123456");
failures += !test(10000000       , "10,000,000");
failures += !test(10000000.123456, "10,000,000.123456");
if (failures) {
    console.log(`${failures} test(s) failed`);
} else {
    console.log("All tests passed");
}
.as-console-wrapper {
    max-height: 100% !important;
}

@t.j.crowder pointed out that now that JavaScript has lookbehind (support info), it can be solved in the regular expression itself:

function numberWithCommas(x) {
    return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}

function numberWithCommas(x) {
    return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}

function test(x, expect) {
    const result = numberWithCommas(x);
    const pass = result === expect;
    console.log(`${pass ? "✓" : "ERROR ====>"} ${x} => ${result}`);
    return pass;
}

let failures = 0;
failures += !test(0,               "0");
failures += !test(0.123456,        "0.123456");
failures += !test(100,             "100");
failures += !test(100.123456,      "100.123456");
failures += !test(1000,            "1,000");
failures += !test(1000.123456,     "1,000.123456");
failures += !test(10000,           "10,000");
failures += !test(10000.123456,    "10,000.123456");
failures += !test(100000,          "100,000");
failures += !test(100000.123456,   "100,000.123456");
failures += !test(1000000,         "1,000,000");
failures += !test(1000000.123456,  "1,000,000.123456");
failures += !test(10000000,        "10,000,000");
failures += !test(10000000.123456, "10,000,000.123456");
if (failures) {
    console.log(`${failures} test(s) failed`);
} else {
    console.log("All tests passed");
}
.as-console-wrapper {
    max-height: 100% !important;
}

(?<!\.\d*) is a negative lookbehind that says the match can't be preceded by a . followed by zero or more digits. The negative lookbehind is faster than the split and join solution (comparison), at least in V8.

Solution 2

I'm surprised nobody mentioned Number.prototype.toLocaleString. It's implemented in JavaScript 1.5 (which was introduced in 1999) so it's basically supported across all major browsers.

var n = 34523453.345;
console.log(n.toLocaleString());    // "34,523,453.345"

It also works in Node.js as of v0.12 via inclusion of Intl

If you want something different, Numeral.js might be interesting.

Solution 3

Below are two different browser APIs that can transform Numbers into structured Strings. Keep in mind that not all users' machines have a locale that uses commas in numbers. To enforce commas to the output, any "western" locale may be used, such as en-US

let number = 1234567890; // Example number to be converted

⚠️ Mind that javascript has a maximum integer value of 9007199254740991


toLocaleString

// default behaviour on a machine with a local that uses commas for numbers
let number = 1234567890;
number.toLocaleString(); // "1,234,567,890"

// With custom settings, forcing a "US" locale to guarantee commas in output
let number2 = 1234.56789; // floating point example
number2.toLocaleString('en-US', {maximumFractionDigits:2}) // "1,234.57"

NumberFormat

let number = 1234567890;
let nf = new Intl.NumberFormat('en-US');
nf.format(number); // "1,234,567,890"

From what I checked (Firefox at least) they are both more or less same regarding performance.

Live demo: https://codepen.io/vsync/pen/MWjdbgL?editors=1000

Solution 4

I suggest using phpjs.org 's number_format()

function number_format(number, decimals, dec_point, thousands_sep) {
    // http://kevin.vanzonneveld.net
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://getsprink.com)
    // +     bugfix by: Benjamin Lupton
    // +     bugfix by: Allan Jensen (http://www.winternet.no)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +     bugfix by: Howard Yeend
    // +    revised by: Luke Smith (http://lucassmith.name)
    // +     bugfix by: Diogo Resende
    // +     bugfix by: Rival
    // +      input by: Kheang Hok Chin (http://www.distantia.ca/)
    // +   improved by: davook
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Jay Klehr
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Amir Habibi (http://www.residence-mixte.com/)
    // +     bugfix by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Theriault
    // +   improved by: Drew Noakes
    // *     example 1: number_format(1234.56);
    // *     returns 1: '1,235'
    // *     example 2: number_format(1234.56, 2, ',', ' ');
    // *     returns 2: '1 234,56'
    // *     example 3: number_format(1234.5678, 2, '.', '');
    // *     returns 3: '1234.57'
    // *     example 4: number_format(67, 2, ',', '.');
    // *     returns 4: '67,00'
    // *     example 5: number_format(1000);
    // *     returns 5: '1,000'
    // *     example 6: number_format(67.311, 2);
    // *     returns 6: '67.31'
    // *     example 7: number_format(1000.55, 1);
    // *     returns 7: '1,000.6'
    // *     example 8: number_format(67000, 5, ',', '.');
    // *     returns 8: '67.000,00000'
    // *     example 9: number_format(0.9, 0);
    // *     returns 9: '1'
    // *    example 10: number_format('1.20', 2);
    // *    returns 10: '1.20'
    // *    example 11: number_format('1.20', 4);
    // *    returns 11: '1.2000'
    // *    example 12: number_format('1.2000', 3);
    // *    returns 12: '1.200'
    var n = !isFinite(+number) ? 0 : +number, 
        prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
        sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
        dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
        toFixedFix = function (n, prec) {
            // Fix for IE parseFloat(0.55).toFixed(0) = 0;
            var k = Math.pow(10, prec);
            return Math.round(n * k) / k;
        },
        s = (prec ? toFixedFix(n, prec) : Math.round(n)).toString().split('.');
    if (s[0].length > 3) {
        s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
    }
    if ((s[1] || '').length < prec) {
        s[1] = s[1] || '';
        s[1] += new Array(prec - s[1].length + 1).join('0');
    }
    return s.join(dec);
}

UPDATE 02/13/14

People have been reporting this doesn't work as expected, so I did a JS Fiddle that includes automated tests.

Update 26/11/2017

Here's that fiddle as a Stack Snippet with slightly modified output:

function number_format(number, decimals, dec_point, thousands_sep) {
    // http://kevin.vanzonneveld.net
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://getsprink.com)
    // +     bugfix by: Benjamin Lupton
    // +     bugfix by: Allan Jensen (http://www.winternet.no)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +     bugfix by: Howard Yeend
    // +    revised by: Luke Smith (http://lucassmith.name)
    // +     bugfix by: Diogo Resende
    // +     bugfix by: Rival
    // +      input by: Kheang Hok Chin (http://www.distantia.ca/)
    // +   improved by: davook
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Jay Klehr
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Amir Habibi (http://www.residence-mixte.com/)
    // +     bugfix by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Theriault
    // +   improved by: Drew Noakes
    // *     example 1: number_format(1234.56);
    // *     returns 1: '1,235'
    // *     example 2: number_format(1234.56, 2, ',', ' ');
    // *     returns 2: '1 234,56'
    // *     example 3: number_format(1234.5678, 2, '.', '');
    // *     returns 3: '1234.57'
    // *     example 4: number_format(67, 2, ',', '.');
    // *     returns 4: '67,00'
    // *     example 5: number_format(1000);
    // *     returns 5: '1,000'
    // *     example 6: number_format(67.311, 2);
    // *     returns 6: '67.31'
    // *     example 7: number_format(1000.55, 1);
    // *     returns 7: '1,000.6'
    // *     example 8: number_format(67000, 5, ',', '.');
    // *     returns 8: '67.000,00000'
    // *     example 9: number_format(0.9, 0);
    // *     returns 9: '1'
    // *    example 10: number_format('1.20', 2);
    // *    returns 10: '1.20'
    // *    example 11: number_format('1.20', 4);
    // *    returns 11: '1.2000'
    // *    example 12: number_format('1.2000', 3);
    // *    returns 12: '1.200'
    var n = !isFinite(+number) ? 0 : +number, 
        prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
        sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
        dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
        toFixedFix = function (n, prec) {
            // Fix for IE parseFloat(0.55).toFixed(0) = 0;
            var k = Math.pow(10, prec);
            return Math.round(n * k) / k;
        },
        s = (prec ? toFixedFix(n, prec) : Math.round(n)).toString().split('.');
    if (s[0].length > 3) {
        s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
    }
    if ((s[1] || '').length < prec) {
        s[1] = s[1] || '';
        s[1] += new Array(prec - s[1].length + 1).join('0');
    }
    return s.join(dec);
}

var exampleNumber = 1;
function test(expected, number, decimals, dec_point, thousands_sep)
{
    var actual = number_format(number, decimals, dec_point, thousands_sep);
    console.log(
        'Test case ' + exampleNumber + ': ' +
        '(decimals: ' + (typeof decimals === 'undefined' ? '(default)' : decimals) +
        ', dec_point: "' + (typeof dec_point === 'undefined' ? '(default)' : dec_point) + '"' +
        ', thousands_sep: "' + (typeof thousands_sep === 'undefined' ? '(default)' : thousands_sep) + '")'
    );
    console.log('  => ' + (actual === expected ? 'Passed' : 'FAILED') + ', got "' + actual + '", expected "' + expected + '".');
    exampleNumber++;
}

test('1,235',    1234.56);
test('1 234,56', 1234.56, 2, ',', ' ');
test('1234.57',  1234.5678, 2, '.', '');
test('67,00',    67, 2, ',', '.');
test('1,000',    1000);
test('67.31',    67.311, 2);
test('1,000.6',  1000.55, 1);
test('67.000,00000', 67000, 5, ',', '.');
test('1',        0.9, 0);
test('1.20',     '1.20', 2);
test('1.2000',   '1.20', 4);
test('1.200',    '1.2000', 3);
.as-console-wrapper {
  max-height: 100% !important;
}

Solution 5

This is a variation of @mikez302's answer, but modified to support numbers with decimals (per @neu-rah's feedback that numberWithCommas(12345.6789) -> "12,345.6,789" instead of "12,345.6789"

function numberWithCommas(n) {
    var parts=n.toString().split(".");
    return parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",") + (parts[1] ? "." + parts[1] : "");
}
Share:
1,699,246
Elias Zamaria
Author by

Elias Zamaria

SOreadytohelp

Updated on December 10, 2021

Comments

  • Elias Zamaria
    Elias Zamaria over 2 years

    I am trying to print an integer in JavaScript with commas as thousands separators. For example, I want to show the number 1234567 as "1,234,567". How would I go about doing this?

    Here is how I am doing it:

    function numberWithCommas(x) {
        x = x.toString();
        var pattern = /(-?\d+)(\d{3})/;
        while (pattern.test(x))
            x = x.replace(pattern, "$1,$2");
        return x;
    }
    

    Is there a simpler or more elegant way to do it? It would be nice if it works with floats also, but that is not necessary. It does not need to be locale-specific to decide between periods and commas.

  • Eric Petroelje
    Eric Petroelje about 12 years
    Very cool, did notice that it has problems with numbers that have more than 3 places after the decimal point though.
  • neu-rah
    neu-rah almost 12 years
    try numberWithCommas(12345.6789) -> "12,345.6,789" i dont like it
  • Aki143S
    Aki143S over 11 years
    Hi.. This example is great. But it will put commas for the decimal part too. just an edit: function formatNumber( num ) { var decimalPart = ''; num = num.toString(); if ( num.indexOf( '.' ) != -1 ) { decimalPart = '.'+ num.split( '.' )[1]; num = parseInt(num.split( '.' )[0]); } var array = num.toString().split( '' ); var index = -3; while ( array.length + index > 0 ) { array.splice( index, 0, ',' ); // Decrement by 4 since we just added another unit to the array. index -= 4; } return array.join( '' ) + decimalPart; };
  • balexandre
    balexandre over 11 years
    to make it more readable, never use 2 returns, instead do: return myString.length > 3 ? myString.substring(0, myString.length - 3) + "," + myString.substring(myString.length - 3) : myString;
  • Mugen
    Mugen about 11 years
    It doesn't work well with fractions, we wouldn't want to have commas after the decimal point
  • Dmitrij Golubev
    Dmitrij Golubev almost 11 years
    Small improvement that fix after '.' problem '123456789.01234'.replace(/\B(?=(?=\d*\.)(\d{3})+(?!\d))/g, '_')
  • Andrew S
    Andrew S almost 11 years
    Not that I marked it down, but I believe people did so because it does not work. As it stands I find even some of your tests to not work.
  • Kerry Jones
    Kerry Jones almost 11 years
    @Andrew S -- Only 1 person has marked it down. It does work, I have used it in my own code many times. It's also not my code (nor my tests), I referenced the site that it comes from, which is a well-known site. Perhaps they have an updated version of it) as the code you are looking at is 3 years old.
  • vbwx
    vbwx almost 11 years
    Very nice, I borrowed the first method ;) But it does not produce a correct result when you want to use a European format and the number is fractional. Line 5 should be: var parts = this.toFixed(decimals).toString().split('.');
  • None
    None almost 11 years
    You are right! toFixed() changes the comma to a period and so the '.' should be used instead of var dec_point. Thanks for pointing that out.
  • csigrist
    csigrist almost 11 years
    I upvoted, but upon further research found some problems with this answer. The performance is not as good as a regex formatter. See the following link: jsperf.com/number-formatting-with-commas. Also trying to get trailing zeros as decimal places is problematic.
  • uKolka
    uKolka almost 11 years
    @csigrist Good points, but it's not as bad as it seems. Speed is browser dependent. In FF or Opera it performs good. I sucks in Chrome though. As for zeroes: var number = 123456.000; number.toLocaleString('en-US', {minimumFractionDigits: 2}); "123,456.00" Those options don't work in FF or Safari though.
  • Ahrengot
    Ahrengot almost 11 years
    What a beautiful solution. A simple and nice addon is the seperator as a second argument, as numbers aren't seperated by commas in every language.
  • synthesizerpatel
    synthesizerpatel over 10 years
    Intl.NumberFormat() seems a little more up-to-date since it uses locale doodads.
  • Chris Betti
    Chris Betti over 10 years
    Could you post the solution using CSS only & spans?
  • chovy
    chovy over 10 years
    can you make an npm module for this?
  • Kerry Jones
    Kerry Jones over 10 years
    @ernix - The operator asked for JavaScript, that answer I gave is JavaScript. This is a JavaScript interpretation of a PHP function.
  • T Nguyen
    T Nguyen over 10 years
    The performance difference may or may not be an issue, depending on the context. If used for a giant table of 1000 results then it will be more important but if only used for a single value, the difference is negligible. But the advantage is that it's locale-aware, so someone in Europe would see 34.523.453,345 or 34 523 453,345. This would be more important on a site with visitors from many countries.
  • ernix
    ernix over 10 years
    @Kerry, this number_format('str') returns '0'. I think it's far from interpretation.
  • Kerry Jones
    Kerry Jones over 10 years
    @ernix - it works exactly as expected with the example the OP gave. I put a fiddle so you can see.
  • Kerry Jones
    Kerry Jones over 10 years
    @ernix - Okay, but the point is that it does exactly what the OP asked for. It is from another site (not maintained by me, and I've stated this previously), and when giving it proper variables, it works exactly as stated. If you believe that to be a bug, contact phpjs.org or see if they have an updated version.
  • ryan j
    ryan j about 10 years
    Appending a null string rather than calling .toString() is generally a bit faster, so you can return (x+'').replace(/\B(?=(\d{3})+(?!\d))/g, ','); Also doesn't throw a JS error if you pass it null or undefined, but typecasting does mean you turn undefined into the string 'undefined' etc. which is something to be aware of.
  • ryan j
    ryan j about 10 years
    was going to do a quick jsfiddle of append vs .toString() but somebody else got there first! if you're curious jsperf.com/tostring-vs-appending-empty-string/3
  • hypno7oad
    hypno7oad about 10 years
    If you change the first line to var var parts = (x) ? (x+"").split(".") : [""];, then you can pass this as a map function over an array of values like so [1000].map(numberWithCommas).
  • Elias Zamaria
    Elias Zamaria about 10 years
    @hypno7oad, my function seems to work fine with map the way it is now. Your change just seems to make it behave strangely when 0 is passed.
  • hypno7oad
    hypno7oad about 10 years
    @EliasZamaria I was trying to handle undefined or null values, since the current function throws errors when it receives them. This could be important when working with datasets that include them, which is how I stumbled upon this thread. BTW, thanks for the answer. It helped me out with my current task. However, you're right that my addition breaks down when it encounters 0. For the sake of brevity, I tried to rely solely on the 'truthiness' of x, which obviously was wrong since 0 is falsy. Try this instead var parts = (x || x === 0) ? (x+"").split(".") : [""];
  • wrossmck
    wrossmck about 10 years
    this doesn't work for -123456 it gives -1,234,56
  • wrossmck
    wrossmck about 10 years
    this doesn't work with format(-123456) it gives -,123,456
  • wrossmck
    wrossmck about 10 years
    this doesn't work with commafy(-123456) it gives -,123,456
  • Wayne
    Wayne about 10 years
    Fixed (although there is probably a more elegant way to do it without checking for the sign every time). In any event, the update makes this work with negative numbers.
  • wrossmck
    wrossmck about 10 years
    that still doesn't work when the input is -123456. it gives -,123,456 jsfiddle.net/wrossmck/2R8mD/1
  • eliajf
    eliajf about 10 years
    I improved this to also handle exponential notation and thought I'd share it: var parts = x.toString().split("."); var e = false; if (parts.length == 1) { parts = x.toString().split("E"); e = true; } parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); return (e ? parts.join("E") : parts.join("."));
  • Petr Havlik
    Petr Havlik almost 10 years
    this is just crazy :)
  • Ryan Shea
    Ryan Shea almost 10 years
    This is fine but it's specific to countries that use commas as thousand separators (many use periods). Take a look at @ukolka's solution, which uses the builtin function toLocaleString() and auto-converts a number based on the locale.
  • Tomáš Zato
    Tomáš Zato almost 10 years
    Awesome. Finally an answer with native function. And what more, this one displays properly in different countries with different separators (in Czech Republic we write X XXX XXX,YYY).
  • Ariel
    Ariel almost 10 years
    @J.Money The .toString is unnecessary, toFixed already returns a string.
  • Elias Zamaria
    Elias Zamaria over 9 years
    What does this do that my answer doesn't? The regex looks slightly different but it looks like it should do the same thing.
  • Shawn J. Molloy
    Shawn J. Molloy over 9 years
    Haha I agree with petr its fun to look at though.
  • Admin
    Admin over 9 years
    This could possibly be an answer to an independent question. You are encouraged to answer your own question, to share knowledge
  • Blaine Kasten
    Blaine Kasten over 9 years
    While this would be awesome if we could use a simple built-in function, it has terrible browser implementation. For Example, IE 8-10 and all Safari doesnt support this
  • ROMANIA_engineer
    ROMANIA_engineer over 9 years
    For var n = 12345.54321 Chrome prints 12,345.543 without 2 decimals.
  • uKolka
    uKolka over 9 years
    @curiosu please read the docs and previous comments. var number = 12345.54321; number.toLocaleString('en-US', {maximumFractionDigits: 10});
  • traditional
    traditional over 9 years
    This is elegant. Exactly what I was looking for.
  • Mahn
    Mahn over 9 years
    @BlaineKasten there's a fully compatible polyfill for older browsers available here: github.com/andyearnshaw/Intl.js it's huge, but it works.
  • Vlad
    Vlad over 9 years
    From blog.tompawlak.org/number-currency-formatting-javascript? Known issue: formatNumber(0.123456) = 0.123,456 The absence of lookbehind in JS makes it difficult to fix it with an elegant regex.
  • Vlad
    Vlad over 9 years
    @DmitrijGolubev Doesn't work for integers. Perhaps forcing the decimal point would be the solution.
  • 2540625
    2540625 about 9 years
    @DmitrijGolubev: Your solution works for the number 42, but fails for the number 2398512. Any idea why?
  • hoju
    hoju almost 9 years
    toLocaleString() has poor mobile support, better to use your own javascript function for now
  • steampowered
    steampowered almost 9 years
    this inspired me to npm install numeral
  • The_Black_Smurf
    The_Black_Smurf almost 9 years
    The OP was asking for a coma separator. As many languages doesn't use coma as thousands separator, you may want to use a specific language that has a coma separator: toLocaleString('en')
  • Marcio
    Marcio almost 9 years
    works only well if you dont have float number with more than 3 numbers after the separator in this case a dot. Otherwise it adds a comma also. "1234567890.1234567890".replace(/\B(?=(\d{3})+\b)/g, ",") This would not work for example. Returns "1,234,567,890.1,234,567,890"
  • vsync
    vsync over 8 years
    I don't know why you've mentioned PHP here at all, or given a prototipical function which already exists
  • None
    None over 8 years
    @vsync The most important reason of all... browser compatibility.
  • vsync
    vsync over 8 years
    @J.Money - first, it doesn't explain what PHP has to do with that, and second, you didn't mention that numberFormat is already in ES4, and what browsers does it supports: chrome 24+, FF 29+, IE11+. only then you should provide an optional polyfill, whom not everyone needs.
  • Kyle Chadha
    Kyle Chadha over 8 years
    Works well for currency though! Just round your digits prior to adding commas.
  • fregante
    fregante over 8 years
    This is great! Thanks for putting together the jsperf
  • Kevin
    Kevin over 8 years
    Available via a pollyfill CDN (only returns what is needed based on useragent): cdn.polyfill.io/v2/polyfill.min.js?features=Intl
  • srobinson
    srobinson over 8 years
    Update for googlers: toLocaleString works in Node.js as of v0.12 via the inclusion of Intl.
  • BrunoLM
    BrunoLM over 8 years
    This website, regex101 explains regex expressions. Might help.
  • FeFiFoFu
    FeFiFoFu over 8 years
    I added s.toString() at beginning of function so it can accept numbers too, not just strings. This is my preferred answer because it is readable, concise, and has none of the bugs the regex answers seem to have.
  • NiCk Newman
    NiCk Newman over 8 years
    This snippet is an absolute monster, out performs everything.
  • MSC
    MSC about 8 years
    Hmm. Can't get it to work in Chrome 48 console. I type:"1234567".toLocaleString('en-US', {minimumFractionDigits: 2}); and get "1234567".
  • uKolka
    uKolka about 8 years
    @MSC you should try parseInt("1234567", 10).toLocaleString('en-US', {minimumFractionDigits: 2}) or new Number("1234567").toLocaleString('en-US', {minimumFractionDigits: 2}) instead. It doesn't work because you use it on a string, not a number.
  • vsync
    vsync about 8 years
    Browsers support is always mentioned at the bottom of each MDN page, which I've linked to.
  • Dennis Heiden
    Dennis Heiden about 8 years
    Perfect for me, just added a new line to remove formatting before processing.
  • Stefan Steiger
    Stefan Steiger about 8 years
    If you add x = parseFloat(x).toFixed(2) then you can ensure there's never more than 3 digits after the decimal point
  • Debashis
    Debashis almost 8 years
    this allows characters like abcdef and so on.. which should be restricted.
  • Debashis
    Debashis almost 8 years
    this allows characters like abcdef and so on.. which should be restricted
  • Dandalf
    Dandalf almost 8 years
    You could still show commas while allowing copy/paste the input in the original format by using css: .thousandsSeparator:before{ content: ','; } JSFiddle: jsfiddle.net/Dandalf/ze6agw7v
  • Marcin Zukowski
    Marcin Zukowski almost 8 years
    Oh wait. It just adds a comma before. Your still render using the Javascript from my example. So I'm not sure what you mean.
  • dandavis
    dandavis almost 8 years
    basic toLocaleString works on safari, options don't
  • yar1
    yar1 almost 8 years
    Downvoted because answer should just be Number(n).toLocaleString()
  • Henders
    Henders almost 8 years
    @yar1 what if you are planning on using that in Safari? Then you just get the number back with no commas.
  • Will Bowman
    Will Bowman over 7 years
    Had issues formatting large numbers with toLocaleString, this worked great (with polyfill)
  • mayatron
    mayatron over 7 years
    As others have noted, number.toLocaleString does not work for all browsers, nor in PhantomJS. Number.toLocaleString() doesn't apply appropriate formatting
  • mayatron
    mayatron over 7 years
    Also number.toLocaleString does not work in PhantomJS. Number.toLocaleString() doesn't apply appropriate formatting
  • Josh
    Josh over 7 years
    @jcollum Could you specify which version of Node you're referring to? I'm working with v4.4.7 and it's works perfectly.
  • David
    David over 7 years
    If you add zero decimal e.g. 0.30000000004 it returns 0.30,000,000,004
  • Elias Zamaria
    Elias Zamaria over 7 years
    @David, did you read my whole answer, including the part where I mention neu-rah's observation that it inserts commas in undesirable places, and my fix for that?
  • Mike 'Pomax' Kamermans
    Mike 'Pomax' Kamermans over 7 years
    the toLocaleString solution should probably also include the desired locale, so toLocaleString("en"), because the English pattern uses commas. However, if toLocaleString() without locale indicator is run in France, then it'll yield periods instead of commas because that's what is used to separate thousands locally.
  • Andy
    Andy about 7 years
    Be aware that the polyfill only works with numbers that have at most 3 decimals. For eg: 0.12345 will output 0.12,345. A good implementation for this can be found in the underscore.string
  • Sinan
    Sinan about 7 years
    you're right, putting a value > 1000 to the if condition fixes that case, however this was a poc and of course better tested versions can be found elsewhere, thanks for pointing out.
  • Andy
    Andy about 7 years
    It's not sufficient to put value > 1000, because it would be the same for any number and more than 3 decimals. eg 1000.12345 returns 1,000.12,345. Your answer is great and on the right path, but just not complete. I was only trying to point out for other people that may stumble on your answer and just copy/pasta it without testing with different input data.
  • Sinan
    Sinan about 7 years
    alright, this needed another edit :) I agree, but now at least it should work for the most cases.
  • adi518
    adi518 about 7 years
    @EliasZamaria I don't understand why you didn't incorporate neu-rah's solution, but rather offer it as an improvement? I'd understand if that solution involved more than a few lines of code.
  • Admin
    Admin about 7 years
    I've updated this since JavaScript's parseFloat stops at the first comma it sees, so if you passed in a string with commas, it would not see the entire string as a number... jsfiddle.net/xc3qh35z/63 and I needed a function that could handle the text values from HTML input elements, which will often already have commas and decimal points in them. The modification removes ALL non-numeric characters except decimal points before generating the new string, so it should handle US numbers other than currency out of the box.
  • Shrijan Tiwari
    Shrijan Tiwari almost 7 years
    It Adds , After Decimal point : 12.3456".replace(/\B(?=(\d{3})+\b)/g, ",") == 12.3,456
  • Donald Duck
    Donald Duck almost 7 years
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value.
  • parlad
    parlad almost 7 years
    hi guys , i implement this for textbox and i can only enter 5 digit , any specific reason? Not good with regex. (not saying this is wrong, just need clue ) Thanks
  • kenberkeley
    kenberkeley almost 7 years
    123456789.123456789.toString().replace(/(\d)(?=(\d{3})+\.)/g‌​, '$1,') => 123,456,789.12345679
  • James Hulse
    James Hulse almost 7 years
    I have taken this a step further by displaying un-selectable commas: jsfiddle.net/ovfd83py/13
  • ush189
    ush189 over 6 years
    Link does not work anymore. But looks similar to this one: openexchangerates.github.io/accounting.js
  • Elias Zamaria
    Elias Zamaria over 6 years
    This doesn't work. I ran makedollars(12345.67) and got an array of strings: ["12", "345.67", ".67"].
  • Peter S McIntyre
    Peter S McIntyre over 6 years
    I should have split on "." Then added the second part back In after. I forgot about decimals.
  • A-Diddy
    A-Diddy over 6 years
    As of this comment, .toLocaleString() doesn't appear to work in the latest version of Chrome (v60.0).
  • Stephen
    Stephen over 6 years
    @A-Diddy just tried this in Chrtome 61 and it works fine
  • A-Diddy
    A-Diddy over 6 years
    Skuld & all, wth... I just confirmed that you're correct, .toLocalString() appears to now work as described above in Chrome v61. How weird that v61 comes out with support minutes after I posted that comment. Talk about Big Brother... Google engineers are on it.
  • Dustin Sun
    Dustin Sun over 6 years
    My answer was updated in response to your comments. Thanks!
  • SharpC
    SharpC over 6 years
    minimumFractionDigits: 2 can also be added to ensure a fixed number of decimal places stackoverflow.com/questions/31581011/…
  • Ralph David Abernathy
    Ralph David Abernathy over 6 years
    Nice script, but it does not work with negative numbers.
  • Young-hwi
    Young-hwi about 6 years
    If the intention is displaying 123456.789 as "١٢٣٬٤٥٦٫٧٨٩" when language code is "ar", "၁၂၃,၄၅၆.၇၈၉" for language code "my", then using `.toLocaleString()'. However, if you just want to insert thousand separating commas, then you will want to use Devin G Rhode's solution.
  • oz.
    oz. about 6 years
    This one is perfect except for one issue. It would be better if there was an extra, optional argument for rounding vs. truncating. Rounding isn't always the desired approach.
  • Elias Zamaria
    Elias Zamaria almost 6 years
    I think it would be a good idea to make this function take its input as a parameter instead of relying on a global variable named cash. And what is the 'Ksh ' for in the return statement?
  • Elias Zamaria
    Elias Zamaria almost 6 years
    I tried commas(12345.67) and got "12,345.6700000000000728".
  • aleclarson
    aleclarson almost 6 years
    @EliasZamaria whoops! forgot about imprecise arithmetic. fixed :)
  • Jacob Stamm
    Jacob Stamm almost 6 years
  • Jared Smith
    Jared Smith almost 6 years
    This is the correct, portable, native answer. Wish I could upvote more than once.
  • jeesty
    jeesty almost 6 years
    the answer num.toLocaleString() below is more correct, because it is built-in and because it accounts for cultural differences. Europe uses commas in numbers differently from the US
  • Jghorton14
    Jghorton14 almost 6 years
    The parenthesis in (x) is not necessary. If only one variable is present x will suffice.
  • Tharanga
    Tharanga almost 6 years
    This piece of JS is awesome. It's better than using toLocaleString() because toLocaleString() does not affect on the numbers placed in another string. (eg: The value is greater than 15000 but ...) but this piece of code does. Thanks.
  • Edgar Quintero
    Edgar Quintero almost 6 years
    This doesn't work if you are dynamically typing. If you just give it a value it works, but if you're feeding a value constantly dynamically the commas are added in the wrong place.
  • Tính Ngô Quang
    Tính Ngô Quang almost 6 years
    I have updated the demo below my answer. When entering a dynamic value in a textbox. you test try @EdgarQuintero
  • Hanna
    Hanna over 5 years
    While 9007199254740991 is the Number.MAX_SAFE_INTEGER you can use toLocaleString() up to Number.MAX_VALUE which is 1.7976931348623157e+308. Anything larger than this will return .
  • codingguy3000
    codingguy3000 over 5 years
    This doesn't work... Consider... 9429700000 With this code you get 9,429,700,0000
  • mmla
    mmla over 5 years
    Not a good option if you're a functional programmer because it does not work with literal number objects, just variables.
  • uKolka
    uKolka over 5 years
    @mmla By option you mean JavaScript? It's a mixed-paradigm language. This does work on literals, maybe not in ways you're expecting. Try (123123).toLocaleString()
  • Shanteshwar Inde
    Shanteshwar Inde about 5 years
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value.Read this.
  • Bathri Nathan
    Bathri Nathan about 5 years
    @ShanteshwarInde i will add additional context to improve the answer sure
  • Arad
    Arad almost 5 years
    Thank you, sir. This is exactly what I needed.
  • Jignesh Sanghani
    Jignesh Sanghani almost 5 years
    fn.substr(0, i) replace with n.substr(0, i) and also number.toFixed(dp).split('.')[1] replace with parseFloat(number).toFixed(dp).split('.')[1]. because when i use directly it's give me en error. please update your code
  • OG Sean
    OG Sean almost 5 years
    why did you write the function with const and => ?
  • Igor Bykov
    Igor Bykov almost 5 years
    @OGSean I always do since it's the most convenient way of declaring variables & functions. Also, I think it helps to keep the code cleaner & shorter.
  • mjs
    mjs over 4 years
    flawed. number grows. an exampel call would have been great!
  • mjs
    mjs over 4 years
    switching ceil to floor fixed that but unsure what other issues will arise.
  • Im Batman
    Im Batman over 4 years
    Note. this won't work with Android if you are developing React Native app.
  • bmacnaughton
    bmacnaughton over 4 years
    The Number(n).toLocaleString() seems the best answer but you'd probably want to use something like new Intl.NumberFormat('en-US').format(n) rather than stripping off dollar signs and decimals if all the user wants is commas in their number.
  • Lonnie Best
    Lonnie Best over 4 years
    @bmacnaughton : That's a good point when you're not dealing with money. However, if you're dealing with money, and just "don't want the leading dollar sign", Number(1000.50).toLocaleString() produces '1,000.5', which removes the insignificant zero that's typically kept when displaying money values. Good comment though: everyone should know what you've said.
  • Elias Zamaria
    Elias Zamaria over 4 years
    This is not code golf. I think it is OK to use some spaces and newlines to make your code more readable, even if they are not strictly required.
  • MMMahdy-PAPION
    MMMahdy-PAPION over 4 years
    @elias-zamaria check it out now by the new function, i removed negative behind look because not support in all browsers
  • mjs
    mjs over 4 years
    i just saw your comment, however, i remember the issue was not that simple. ceil was also flawed in some cases. just checked. if the number is negative. therefore abs should come before floor.
  • mjs
    mjs over 4 years
    Try Math.floor(-75.1) ;)
  • Andrew
    Andrew over 4 years
    Note: You may need to cast your number string to an int or float first, i.e. parseInt(123456, 10).toLocaleString() or parseFloat(123456.789012, 10).toLocaleString(). Also, in the latter case, .toLocaleString() will by default limit the number of decimal digits.
  • moto
    moto about 4 years
    @codingguy3000 The function posted at the end of the answer does not have this problem.
  • T.J. Crowder
    T.J. Crowder about 4 years
    Now that JavaScript is getting lookbehind, the digits-after-the-decimal part can be solved in the regex rather than using split and join: .replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ","). I've updated the answer to show that as well. Being a variable length lookbehind I worried about performance, but it beats split and join.
  • Junius L.
    Junius L. about 4 years
    @T.J.Crowder it's breaking in Safari Invalid regular expression: invalid group specifier name
  • T.J. Crowder
    T.J. Crowder about 4 years
    @JuniusL. - See the support info linked in the answer.
  • doublejosh
    doublejosh about 4 years
    Not a good solution! Use toLocaleString() and stop being North American focused. UGH.
  • Suraj Dalvi
    Suraj Dalvi almost 4 years
    this not working node js. It is not giving response in Indian format
  • Yakov Kemer
    Yakov Kemer almost 4 years
    be careful with this code. it doesn't work in firefox
  • hendr1x
    hendr1x almost 4 years
    Doesnt work with numbers that have long decimal values (just tested with four : ie : 12223.0234)
  • Elias Zamaria
    Elias Zamaria almost 4 years
    @hendr1x what is the problem? This answer mentions that the first snippet puts commas in undesirable places if there are more than 3 digits after the decimal point. The 2nd and 3rd work fine, and return "12,223.0234".
  • Jee Mok
    Jee Mok almost 4 years
    to have two decimals: x.toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  • Aaron Angle
    Aaron Angle over 3 years
    This does not work in safari. Broke my entire application and took me forever to figure out this was the problem
  • Ivan Juarez
    Ivan Juarez over 3 years
    Take your Like~!!
  • pishpish
    pishpish over 3 years
    Keep in mind that toLocaleString is implementation specific - you will get different results across browsers/environments.
  • Mahmood Afzalzadeh
    Mahmood Afzalzadeh over 3 years
    Saved my time and toLocaleString is useful and works on FireFox and Chrome very good.
  • Elias Zamaria
    Elias Zamaria over 3 years
    That is telling me "Uncaught TypeError: number is not iterable". Maybe you need to call toString on the number.
  • Robo Robok
    Robo Robok over 3 years
    @EliasZamaria sorry, I used a string in my case. Updated my answer to convert to string.
  • Elias Zamaria
    Elias Zamaria over 3 years
    I tried 12.34 as the number and it returned 12,.34.
  • Robo Robok
    Robo Robok over 3 years
    I thought it was only meant to work with decimals. Updated for you.
  • beliha
    beliha over 3 years
    Doesn't work in IE11! It has an issue with the regEx for some reason: "Unexpected quantifier"
  • Nader Gharibian Fard
    Nader Gharibian Fard over 3 years
    tnx brother.. var nf = new Intl.NumberFormat(); nf.format(number); // "1,234,567,890" it's work
  • Dzmitry Vasilevsky
    Dzmitry Vasilevsky over 3 years
    @EliasZamaria please edit you answer about lookbehind It can't be used in enterprise still. The latest Safari doesn't support it. And it can't be polyfilled. Many guys above broke their apps because of that suggestion as well as one from my team. Please add that it is not yet ready to use like with uppercase characters.
  • Elias Zamaria
    Elias Zamaria over 3 years
    @DzmitryVasilevsky I didn't add the lookbehind stuff to this answer. I can remove it, but I think it is useful, and this answer still has my original solution, for interpreters that don't support lookbehind.
  • Dzmitry Vasilevsky
    Dzmitry Vasilevsky over 3 years
    @EliasZamaria The fact is that it is causing problems for seekers. I don't think this should be removed but rather edited like: t.j.crowder pointed out that there is a cutting edge way to solve that. Be aware that it is STILL NOT SUPPORTED by some of the modern browsers - support info)! Modern JavaScript has lookbehind, it can be solved in the regular expression itself...
  • Elias Zamaria
    Elias Zamaria about 3 years
    @DzmitryVasilevsky is the "support info" link that is there now good enough? Or does it need to be made more obvious and noticeable?
  • Dzmitry Vasilevsky
    Dzmitry Vasilevsky about 3 years
    @EliasZamaria I think should be more noticeable. BTW thanks for cooperation
  • Hissvard
    Hissvard about 3 years
    This is perfect. I kept looking and only finding incredibly bloated libraries that didn't even let me change thousands and decimal separator.
  • Maor Ben
    Maor Ben about 3 years
    You could also loop through x.toString().length and use x[i]
  • serraosays
    serraosays about 3 years
    This is the right answer, people are wildly overcomplicating this. toLocaleString() is built for this exact use case.
  • Elias Zamaria
    Elias Zamaria almost 3 years
    What is that supposed to do? I get errors like "123.split is not a function".
  • anarchist
    anarchist almost 3 years
    You can use it to convert comma string values to fractional numbers. console.log(parseFloat(parseFloat(('123,56').split(',').join‌​('.')).toFixed(2)));
  • Andy Borgmann
    Andy Borgmann almost 3 years
    This was my favorite of the solutions.
  • Elias Zamaria
    Elias Zamaria almost 3 years
    FYI it looks like that is already mentioned here: stackoverflow.com/a/32154217/28324
  • ladieu
    ladieu over 2 years
    only works if you pass it a string.. an easy fix but just wanted to note it
  • Cees Timmerman
    Cees Timmerman over 2 years
    Instead of 'en-US', you could use navigator.userLanguage. Legacy browser support.
  • AncientSwordRage
    AncientSwordRage over 2 years
    I think adding a link to toLocaleString, uKolka's answer or explaining why this might be better than that function would be good.
  • AncientSwordRage
    AncientSwordRage over 2 years
    The currency formatting massively skews this: jsben.ch/u4gk0
  • kmsheng
    kmsheng about 2 years
    This solution DOES NOT work for safari and iphone chrome. It will break your entire application and it's hard to debug in mobile. Please consider down-voting this solution to save people's time.
  • Adam Ellis
    Adam Ellis almost 2 years
    The answer is good for some browsers, but since it doesn't work in Safari on Mac/iOS (as of June 2022), it should not be the top accepted answer. A big obvious note should be added to say that it shouldn't be used in a production environment.
  • Cybernetic
    Cybernetic almost 2 years
    None of these work with the number 57024.00000000001
  • Elias Zamaria
    Elias Zamaria almost 2 years
    @Cybernetic I just ran (57024.00000000001).toString().replace(/\B(?<!\.\d*)(?=(\d{3‌​})+(?!\d))/g, ",") and got 57,024.00000000001.
  • Cybernetic
    Cybernetic almost 2 years
    @EliasZamaria interesting....I get 57,024.00,000,000,001 .... I am on Chrome Version 102.0.5005.115 (Official Build) (arm64)
  • Elias Zamaria
    Elias Zamaria almost 2 years
    @Cybernetic are you sure you are using one of the last 2 numberWithCommas functions in this answer (not the first)?
  • Cybernetic
    Cybernetic almost 2 years
    @EliasZamaria you're correct, my apologies. I was using (x).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");