Categories
formatting integer javascript numbers

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

2418

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.

12

  • 293

    Number(x).toLocaleString()

    – Boffin

    Mar 9, 2015 at 23:04

  • 65

    Worth noting that Number.prototype.toLocaleString still does not work in Safari, in 2016. Instead of actually formatting the number, it just returns it, no error thrown. Having the biggest facepalm today as a result of that… #goodworkApple

    – aendra

    Mar 10, 2016 at 11:21


  • 6

    @atomless Really? I have Version 61.0.3163.100 (Official Build) (64-bit) and when I say (2982932323).toLocaleString() the response is "2,982,932,323". My default locale is en-US. Maybe you should try (2982932323).toLocaleString('en-US').

    – Ray Toal

    Nov 2, 2017 at 2:46

  • 2

    Seems like this is ought to be the approved answer… Intl.NumberFormat(‘en-US’).format(count) See developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

    Oct 28, 2019 at 23:19

  • 8

    Number.prototype.toLocaleString does work in Safari 14, in 2021.

    – Martijn

    Apr 21, 2021 at 11:36


3646

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.

51

  • 31

    Very cool, did notice that it has problems with numbers that have more than 3 places after the decimal point though.

    Feb 23, 2012 at 18:34

  • 78

    try numberWithCommas(12345.6789) -> “12,345.6,789” i dont like it

    – neu-rah

    May 27, 2012 at 13:28

  • 36

    Small improvement that fix after ‘.’ problem ‘123456789.01234’.replace(/\B(?=(?=\d*\.)(\d{3})+(?!\d))/g, ‘_’)

    Jun 17, 2013 at 9:29


  • 12

    @DmitrijGolubev Doesn’t work for integers. Perhaps forcing the decimal point would be the solution.

    – Vlad

    Jan 20, 2015 at 16:13


  • 22

    This does not work in safari. Broke my entire application and took me forever to figure out this was the problem

    Aug 19, 2020 at 5:19

2739

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.

23

  • 36

    @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.

    – uKolka

    Jul 23, 2013 at 15:50

  • 33

    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.

    – T Nguyen

    Feb 12, 2014 at 21:06

  • 12

    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).

    Jul 7, 2014 at 17:02

  • 29

    Update for googlers: toLocaleString works in Node.js as of v0.12 via the inclusion of Intl.

    – srobinson

    Nov 2, 2015 at 16:59

  • 19

    @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.

    – uKolka

    Feb 22, 2016 at 16:29

436

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

9

  • 6

    Browsers support is always mentioned at the bottom of each MDN page, which I’ve linked to.

    – vsync

    Mar 19, 2016 at 16:38

  • 5

    basic toLocaleString works on safari, options don’t

    – dandavis

    Jun 21, 2016 at 9:16

  • As others have noted, number.toLocaleString does not work for all browsers, nor in PhantomJS. Number.toLocaleString() doesn’t apply appropriate formatting

    – mayatron

    Sep 9, 2016 at 20:37


  • 4

    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.

    Nov 17, 2016 at 18:05

  • 1

    minimumFractionDigits: 2 can also be added to ensure a fixed number of decimal places stackoverflow.com/questions/31581011/…

    – SharpC

    Jan 17, 2018 at 14:35