How to format a number with commas as thousands separators?
Asked Answered
C

52

2697

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;
}

console.log(numberWithCommas(1000))

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.

Calamite answered 24/5, 2010 at 23:42 Comment(2)
toLocaleString is inconsistent and should not be used. For example - on Firefox this will return 1,234 but on IE this will add decimals: 1,234.00Charlatanry
As of 2023, toLocaleString appears to work fine on Safari, and I'm seeing consistent results between Chrome, Safari, and Firefox.Roast
C
3883

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.

Calamite answered 25/5, 2010 at 0:40 Comment(55)
Very cool, did notice that it has problems with numbers that have more than 3 places after the decimal point though.Sirocco
try numberWithCommas(12345.6789) -> "12,345.6,789" i dont like itAmitosis
It doesn't work well with fractions, we wouldn't want to have commas after the decimal pointApparent
Small improvement that fix after '.' problem '123456789.01234'.replace(/\B(?=(?=\d*\.)(\d{3})+(?!\d))/g, '_')Cope
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.Xebec
Intl.NumberFormat() seems a little more up-to-date since it uses locale doodads.Fencer
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.Niela
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/3Niela
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).Efficiency
@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.Calamite
@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(".") : [""];Efficiency
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("."));Weitman
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.Polyphony
@DmitrijGolubev Doesn't work for integers. Perhaps forcing the decimal point would be the solution.Cestoid
@DmitrijGolubev: Your solution works for the number 42, but fails for the number 2398512. Any idea why?Memory
This website, regex101 explains regex expressions. Might help.Facies
If you add x = parseFloat(x).toFixed(2) then you can ensure there's never more than 3 digits after the decimal pointOccam
Downvoted because answer should just be Number(n).toLocaleString()Strikebreaker
@Strikebreaker what if you are planning on using that in Safari? Then you just get the number back with no commas.Lauderdale
If you add zero decimal e.g. 0.30000000004 it returns 0.30,000,000,004Asleyaslope
@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?Calamite
@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.Antiphlogistic
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 ) ThanksRiba
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 USChampagne
The parenthesis in (x) is not necessary. If only one variable is present x will suffice.Highbinder
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.Epicarp
This doesn't work... Consider... 9429700000 With this code you get 9,429,700,0000Eagle
@Eagle The function posted at the end of the answer does not have this problem.Biscuit
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.Pappose
@T.J.Crowder it's breaking in Safari Invalid regular expression: invalid group specifier nameIcily
@JuniusL. - See the support info linked in the answer.Pappose
Not a good solution! Use toLocaleString() and stop being North American focused. UGH.Inverness
be careful with this code. it doesn't work in firefoxManumit
Doesnt work with numbers that have long decimal values (just tested with four : ie : 12223.0234)Sephira
@Sephira 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".Calamite
to have two decimals: x.toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");Seduction
This does not work in safari. Broke my entire application and took me forever to figure out this was the problemLordly
Doesn't work in IE11! It has an issue with the regEx for some reason: "Unexpected quantifier"Guillermoguilloche
@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.Korney
@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.Calamite
@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...Korney
@DzmitryVasilevsky is the "support info" link that is there now good enough? Or does it need to be made more obvious and noticeable?Calamite
@EliasZamaria I think should be more noticeable. BTW thanks for cooperationKorney
I think adding a link to toLocaleString, uKolka's answer or explaining why this might be better than that function would be good.Cockspur
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.Melodeemelodeon
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.Thunderstruck
None of these work with the number 57024.00000000001Helbonnas
@Helbonnas I just ran (57024.00000000001).toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",") and got 57,024.00000000001.Calamite
@EliasZamaria interesting....I get 57,024.00,000,000,001 .... I am on Chrome Version 102.0.5005.115 (Official Build) (arm64)Helbonnas
@Helbonnas are you sure you are using one of the last 2 numberWithCommas functions in this answer (not the first)?Calamite
@EliasZamaria you're correct, my apologies. I was using (x).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");Helbonnas
Split is slow. Try this: jsfiddle.net/woqfgvs3 (uses indexOf)Appleton
This doesn't work in any IOS browser, be warned!!Melendez
This was the same ChatGPTs answer return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");Neoclassic
For decimal numbers, you an split the number by the decimal separator and just AddThousandSeparator to the number part. Then concatenate decimal part with decimal part. Good luck ;)Abbey
B
3144

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.

Bedivere answered 15/7, 2013 at 21:1 Comment(24)
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.Pinot
@Pinot 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.Bedivere
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.Empirin
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).Brief
For var n = 12345.54321 Chrome prints 12,345.543 without 2 decimals.Cacciatore
@curiosu please read the docs and previous comments. var number = 12345.54321; number.toLocaleString('en-US', {maximumFractionDigits: 10});Bedivere
toLocaleString() has poor mobile support, better to use your own javascript function for nowJellied
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')Peterman
Update for googlers: toLocaleString works in Node.js as of v0.12 via the inclusion of Intl.Infusorian
Hmm. Can't get it to work in Chrome 48 console. I type:"1234567".toLocaleString('en-US', {minimumFractionDigits: 2}); and get "1234567".Domela
@Domela 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.Bedivere
Also number.toLocaleString does not work in PhantomJS. Number.toLocaleString() doesn't apply appropriate formattingFlasher
@jcollum Could you specify which version of Node you're referring to? I'm working with v4.4.7 and it's works perfectly.Anatomize
As of this comment, .toLocaleString() doesn't appear to work in the latest version of Chrome (v60.0).Milky
@Milky just tried this in Chrtome 61 and it works fineBissau
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.Milky
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.Duct
Not a good option if you're a functional programmer because it does not work with literal number objects, just variables.Assai
@Assai 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()Bedivere
Note. this won't work with Android if you are developing React Native app.Cheka
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.Roast
Take your Like~!!Brahmana
Keep in mind that toLocaleString is implementation specific - you will get different results across browsers/environments.Betweentimes
If you want to get the format corresponding to the language of the page, you can do toLocaleString(document.documentElement.lang).Marsala
H
559

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 locale 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"

//You can also force a minimum of 2 trailing digits
let number3 = 1.5;
number3.toLocaleString('en-US', {minimumFractionDigits:2, maximumFractionDigits:2}); //"1.50"

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

Hindbrain answered 22/8, 2015 at 8:52 Comment(9)
Browsers support is always mentioned at the bottom of each MDN page, which I've linked to.Hindbrain
basic toLocaleString works on safari, options don'tCheesecake
As others have noted, number.toLocaleString does not work for all browsers, nor in PhantomJS. Number.toLocaleString() doesn't apply appropriate formattingFlasher
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.Penstemon
minimumFractionDigits: 2 can also be added to ensure a fixed number of decimal places #31581511Batik
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 .Hydrolysis
Saved my time and toLocaleString is useful and works on FireFox and Chrome very good.Centeno
tnx brother.. var nf = new Intl.NumberFormat(); nf.format(number); // "1,234,567,890" it's workBloc
Instead of 'en-US', you could use navigator.userLanguage. Legacy browser support.Echikson
B
124

I suggest using phpjs.org 's number_format()

function number_format(number, decimals, dec_point, thousands_sep) {
    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) {
    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;
}
Bagpipe answered 24/5, 2010 at 23:47 Comment(9)
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.Rampant
@Roast 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.Bagpipe
@ernix - The operator asked for JavaScript, that answer I gave is JavaScript. This is a JavaScript interpretation of a PHP function.Bagpipe
@Kerry, this number_format('str') returns '0'. I think it's far from interpretation.Oeo
@Oeo - it works exactly as expected with the example the OP gave. I put a fiddle so you can see.Bagpipe
@Oeo - 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.Bagpipe
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.Handicapped
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.Tackling
This is perfect. I kept looking and only finding incredibly bloated libraries that didn't even let me change thousands and decimal separator.Hutner
N
84

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] : "");
}
Niall answered 5/6, 2012 at 15:3 Comment(1)
Hey look, you're an open-source contributor :)Uticas
B
81
function formatNumber (num) {
    return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,")
}

print(formatNumber(2665));      // 2,665
print(formatNumber(102665));    // 102,665
print(formatNumber(111102665)); // 111,102,665
Benign answered 19/8, 2014 at 6:47 Comment(7)
What does this do that my answer doesn't? The regex looks slightly different but it looks like it should do the same thing.Calamite
This is elegant. Exactly what I was looking for.Gizzard
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.Cestoid
this allows characters like abcdef and so on.. which should be restricted.Angellaangelle
123456789.123456789.toString().replace(/(\d)(?=(\d{3})+\.)/g, '$1,') => 123,456,789.12345679Ravenna
This was my favorite of the solutions.Denbighshire
This doesn't work in older safari (2022 Safari on iOS for example in iOS 16.1.1)Dewy
S
75

Using Regular expression

function toCommas(value) {
    return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
console.log(toCommas(123456789)); // 123,456,789

console.log(toCommas(1234567890)); // 1,234,567,890
console.log(toCommas(1234)); // 1,234

Using toLocaleString()

var number = 123456.789;

// request a currency format
console.log(number.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' }));
// → 123.456,79 €

// the Japanese yen doesn't use a minor unit
console.log(number.toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' }))
// → ¥123,457

// limit to three significant digits
console.log(number.toLocaleString('en-IN', { maximumSignificantDigits: 3 }));
// → 1,23,000

ref MDN:Number.prototype.toLocaleString()

Using Intl.NumberFormat()

var number = 123456.789;

console.log(new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(number));
// expected output: "123.456,79 €"

// the Japanese yen doesn't use a minor unit
console.log(new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }).format(number));
// expected output: "¥123,457"

// limit to three significant digits
console.log(new Intl.NumberFormat('en-IN', { maximumSignificantDigits: 3 }).format(number));

// expected output: "1,23,000"

ref Intl.NumberFormat

DEMO AT HERE

<script type="text/javascript">
  // Using Regular expression
  function toCommas(value) {
    return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  }

  function commas() {
    var num1 = document.myform.number1.value;

    // Using Regular expression
    document.getElementById('result1').value = toCommas(parseInt(num1));
    // Using toLocaleString()

    document.getElementById('result2').value = parseInt(num1).toLocaleString('ja-JP', {
      style: 'currency',
      currency: 'JPY'
    });

    // Using Intl.NumberFormat()
    document.getElementById('result3').value = new Intl.NumberFormat('ja-JP', {
      style: 'currency',
      currency: 'JPY'
    }).format(num1);
  }
</script>
<FORM NAME="myform">
  <INPUT TYPE="text" NAME="number1" VALUE="123456789">
  <br>
  <INPUT TYPE="button" NAME="button" Value="=>" onClick="commas()">
  <br>Using Regular expression
  <br>
  <INPUT TYPE="text" ID="result1" NAME="result1" VALUE="">
  <br>Using toLocaleString()
  <br>
  <INPUT TYPE="text" ID="result2" NAME="result2" VALUE="">
  <br>Using Intl.NumberFormat()
  <br>
  <INPUT TYPE="text" ID="result3" NAME="result3" VALUE="">

</FORM>

Performance

Performance http://jsben.ch/sifRd

Satiny answered 13/7, 2018 at 9:30 Comment(3)
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.Evert
I have updated the demo below my answer. When entering a dynamic value in a textbox. you test try @EdgarQuinteroMeaning
The currency formatting massively skews this: jsben.ch/u4gk0Cockspur
R
48

Intl.NumberFormat

Native JS function. Supported by IE11, Edge, latest Safari, Chrome, Firefox, Opera, Safari on iOS and Chrome on Android.

var number = 3500;

console.log(new Intl.NumberFormat().format(number));
// → '3,500' if in US English locale
Revkah answered 31/10, 2017 at 23:56 Comment(1)
My answer was updated in response to your comments. Thanks!Revkah
M
44

I am quite impressed by the number of answers this question has got. I like the answer by uKolka:

n.toLocaleString()

But unfortunately, in some locales like Spanish, it does not work (IMHO) as expected for numbers below 10,000:

Number(1000).toLocaleString('ES-es')

Gives 1000 and not 1.000.

See toLocaleString not working on numbers less than 10000 in all browsers to know why.

So I had to use the answer by Elias Zamaria choosing the right thousands separator character:

n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, Number(10000).toLocaleString().substring(2, 3))

This one works well as a one-liner for both locales that use , or . as the thousands separator and starts working from 1,000 in all cases.

Number(1000).toString().replace(/\B(?=(\d{3})+(?!\d))/g, Number(10000).toLocaleString().substring(2, 3))

Gives 1.000 with a Spanish locale context.

Should you want to have absolute control over the way a number is formatted, you may also try the following:

let number   = 1234.567
let decimals = 2
let decpoint = '.' // Or Number(0.1).toLocaleString().substring(1, 2)
let thousand = ',' // Or Number(10000).toLocaleString().substring(2, 3)

let n = Math.abs(number).toFixed(decimals).split('.')
n[0] = n[0].split('').reverse().map((c, i, a) =>
  i > 0 && i < a.length && i % 3 == 0 ? c + thousand : c
).reverse().join('')
let final = (Math.sign(number) < 0 ? '-' : '') + n.join(decpoint)

console.log(final)

Gives 1,234.57.

This one does not need a regular expression. It works by adjusting the number to the desired amount of decimals with toFixed first, then dividing it around the decimal point . if there is one. The left side is then turned into an array of digits which is reversed. Then a thousands separator is added every three digits from the start and the result reversed again. The final result is the union of the two parts. The sign of the input number is removed with Math.abs first and then put back if necessary.

It is not a one-liner but not much longer and easily turned into a function. Variables have been added for clarity, but those may be substituted by their desired values if known in advance. You may use the expressions that use toLocaleString as a way to find out the right characters for the decimal point and the thousands separator for the current locale (bear in mind that those require a more modern Javascript.)

Mattie answered 4/11, 2019 at 20:4 Comment(0)
A
35

Thanks to everyone for their replies. I have built off of some of the answers to make a more "one-size-fits-all" solution.

The first snippet adds a function that mimics PHP's number_format() to the Number prototype. If I am formatting a number, I usually want decimal places so the function takes in the number of decimal places to show. Some countries use commas as the decimal and decimals as the thousands separator so the function allows these separators to be set.

Number.prototype.numberFormat = function(decimals, dec_point, thousands_sep) {
    dec_point = typeof dec_point !== 'undefined' ? dec_point : '.';
    thousands_sep = typeof thousands_sep !== 'undefined' ? thousands_sep : ',';

    var parts = this.toFixed(decimals).split('.');
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousands_sep);

    return parts.join(dec_point);
}

You would use this as follows:

var foo = 5000;
console.log(foo.numberFormat(2)); // us format: 5,000.00
console.log(foo.numberFormat(2, ',', '.')); // european format: 5.000,00

I found that I often needed to get the number back for math operations, but parseFloat converts 5,000 to 5, simply taking the first sequence of integer values. So I created my own float conversion function and added it to the String prototype.

String.prototype.getFloat = function(dec_point, thousands_sep) {
    dec_point = typeof dec_point !== 'undefined' ? dec_point : '.';
    thousands_sep = typeof thousands_sep !== 'undefined' ? thousands_sep : ',';

    var parts = this.split(dec_point);
    var re = new RegExp("[" + thousands_sep + "]");
    parts[0] = parts[0].replace(re, '');

    return parseFloat(parts.join(dec_point));
}

Now you can use both functions as follows:

var foo = 5000;
var fooString = foo.numberFormat(2); // The string 5,000.00
var fooFloat = fooString.getFloat(); // The number 5000;

console.log((fooString.getFloat() + 1).numberFormat(2)); // The string 5,001.00
Ar answered 18/9, 2012 at 1:50 Comment(7)
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('.');Granny
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.Ar
can you make an npm module for this?Miguel
@J.Money The .toString is unnecessary, toFixed already returns a string.Westing
I don't know why you've mentioned PHP here at all, or given a prototipical function which already existsHindbrain
@Hindbrain The most important reason of all... browser compatibility.Ar
@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.Hindbrain
H
25

I think this is the shortest regular expression that does it:

/\B(?=(\d{3})+\b)/g

"123456".replace(/\B(?=(\d{3})+\b)/g, ",")

I checked it on a few numbers and it worked.

Hippie answered 13/3, 2015 at 6:31 Comment(3)
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"Chevrette
Works well for currency though! Just round your digits prior to adding commas.Teddie
It Adds , After Decimal point : 12.3456".replace(/\B(?=(\d{3})+\b)/g, ",") == 12.3,456Paraldehyde
D
22

Number.prototype.toLocaleString() would have been awesome if it was provided natively by all browsers (Safari).

I checked all other answers but noone seemed to polyfill it. Here is a poc towards that, which is actually a combination of first two answers; if toLocaleString works it uses it, if it doesn't it uses a custom function.

var putThousandsSeparators;

putThousandsSeparators = function(value, sep) {
  if (sep == null) {
    sep = ',';
  }
  // check if it needs formatting
  if (value.toString() === value.toLocaleString()) {
    // split decimals
    var parts = value.toString().split('.')
    // format whole numbers
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, sep);
    // put them back together
    value = parts[1] ? parts.join('.') : parts[0];
  } else {
    value = value.toLocaleString();
  }
  return value;
};

alert(putThousandsSeparators(1234567.890));
Druci answered 19/5, 2016 at 5:11 Comment(4)
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.stringNeoclassic
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.Druci
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.Neoclassic
alright, this needed another edit :) I agree, but now at least it should work for the most cases.Druci
S
22

You can either use this procedure to format your currency needing.

var nf = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});
nf.format(123456.789); // ‘$123,456.79’

For more info you can access this link.

https://www.justinmccandless.com/post/formatting-currency-in-javascript/

Soma answered 30/11, 2017 at 4:4 Comment(1)
This is the correct, portable, native answer. Wish I could upvote more than once.Valais
C
21

The thousands separator can be inserted in an international-friendly manner using the browser's Intl object:

Intl.NumberFormat().format(1234);
// returns "1,234" if the user's locale is en_US, for example

See MDN's article on NumberFormat for more, you can specify locale behavior or default to the user's. This is a little more foolproof because it respects local differences; many countries use periods to separate digits while a comma denotes the decimals.

Intl.NumberFormat isn't available in all browsers yet, but it works in latest Chrome, Opera, & IE. Firefox's next release should support it. Webkit doesn't seem to have a timeline for implementation.

Circumnavigate answered 26/4, 2014 at 1:43 Comment(4)
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 thisDelfinadelfine
@BlaineKasten there's a fully compatible polyfill for older browsers available here: github.com/andyearnshaw/Intl.js it's huge, but it works.Suckling
Available via a pollyfill CDN (only returns what is needed based on useragent): cdn.polyfill.io/v2/polyfill.min.js?features=IntlHell
Had issues formatting large numbers with toLocaleString, this worked great (with polyfill)Pulchritude
E
16

if you are dealing with currency values and formatting a lot then it might be worth to add tiny accounting.js which handles lot of edge cases and localization:

// Default usage:
accounting.formatMoney(12345678); // $12,345,678.00

// European formatting (custom symbol and separators), could also use options object as second param:
accounting.formatMoney(4999.99, "€", 2, ".", ","); // €4.999,99

// Negative values are formatted nicely, too:
accounting.formatMoney(-500000, "£ ", 0); // £ -500,000

// Simple `format` string allows control of symbol position [%v = value, %s = symbol]:
accounting.formatMoney(5318008, { symbol: "GBP",  format: "%v %s" }); // 5,318,008.00 GBP
Endocrine answered 25/1, 2014 at 17:40 Comment(1)
Link does not work anymore. But looks similar to this one: openexchangerates.github.io/accounting.jsKickapoo
D
15

Here's a simple function that inserts commas for thousand separators. It uses array functions rather than a RegEx.

/**
 * Format a number as a string with commas separating the thousands.
 * @param num - The number to be formatted (e.g. 10000)
 * @return A string representing the formatted number (e.g. "10,000")
 */
var formatNumber = function(num) {
    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('');
};

CodeSandbox link with examples: https://codesandbox.io/s/p38k63w0vq

Doityourself answered 2/5, 2012 at 16:51 Comment(2)
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; };Gab
Thank you, sir. This is exactly what I needed.Inappropriate
E
15

The following code uses char scan, so there's no regex.

function commafy( num){
  var parts = (''+(num<0?-num:num)).split("."), s=parts[0], L, i=L= s.length, o='';
  while(i--){ o = (i===0?'':((L-i)%3?'':',')) 
                  +s.charAt(i) +o }
  return (num<0?'-':'') + o + (parts[1] ? '.' + parts[1] : ''); 
}

It shows promising performance: http://jsperf.com/number-formatting-with-commas/5

2015.4.26: Minor fix to resolve issue when num<0. See https://jsfiddle.net/runsun/p5tqqvs3/

Edo answered 31/7, 2013 at 20:59 Comment(3)
this doesn't work with commafy(-123456) it gives -,123,456Blackfoot
This is great! Thanks for putting together the jsperfEusebiaeusebio
This snippet is an absolute monster, out performs everything.Firelock
A
14

Use This code to handle currency format for india. Country code can be changed to handle other country currency.

let amount =350256.95
var formatter = new Intl.NumberFormat('en-IN', {
  minimumFractionDigits: 2,
});

// Use it.

formatter.format(amount);

output:

3,50,256.95
Alleyway answered 12/3, 2019 at 10:4 Comment(2)
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.Kimsey
@ShanteshwarInde i will add additional context to improve the answer sureAlleyway
C
12

You can also use the Intl.NumberFormat constructor. Here is how you can do it.

 resultNumber = new Intl.NumberFormat('en-IN', { maximumSignificantDigits: 3 }).format(yourNumber); 
Curd answered 28/7, 2019 at 10:51 Comment(1)
this not working node js. It is not giving response in Indian formatScattering
R
12

Universal, fast, accurate, simple function

  • Using RegEx (Fast & Accurate)
  • Support Numbers(Float/Integer)/String/Multiple numbers in a string
  • Smart well (Not grouping decimals - Compatible with different types of grouping)
  • Support all browsers specially 'Safari' & 'IE' & many older browsers
  • [Optional] Respecting non-English (Persian/Arabic) digits (+ Pre-fix)
TL;DR - Full version function (minified):

// num: Number/s (String/Number),
// sep: Thousands separator (String) - Default: ','
// dec: Decimal separator (String) - Default: '.' (Just one char)
// u: Universal support for languages characters (String - RegEx character set / class) - Example: '[\\d\\u0660-\\u0669\\u06f0-\\u06f9]' (English/Persian/Arabic), Default: '\\d' (English)

function formatNums(num,sep,dec,u){sep=sep||',';u=u||'\\d';if(typeof num!='string'){num=String(num);if(dec&&dec!='.')num=num.replace('.',dec);}return num.replace(RegExp('\\'+(dec||'.')+u+'+|'+u+'(?=(?:'+u+'{3})+(?!'+u+'))','g'),function(a){return a.length==1?a+sep:a})}

text='100000000 English or Persian/Arabic ۱۲۳۴۵۶۷۸۹/٠١٢٣٤٥٦٧٨٩ this is 123123123.123123123 with this -123123 and these 10 100 1000 123123/123123 (2000000) .33333 100.00 or any like 500000Kg';

console.log(formatNums(10000000.0012));
console.log(formatNums(10000000.0012,'.',',')); // German
console.log(formatNums(text,',','.','[\\d\\u0660-\\u0669\\u06f0-\\u06f9]')); // Respect Persian/Arabic digits
<input oninput="document.getElementById('result').textContent=formatNums(this.value)" placeholder="Type a number here">
<div id="result"></div>

Why NOT satisfied with other answers?

  • Number.prototype.toLocaleString() / Intl.NumberFormat (Right answer)
    • If no well arguments, we can't expect same result. Also with arguments options we still can't be sure what can be the result because it will use local settings and possible client modifications effect on it or the browser/device not support it.
    • >~2016 browsers support and still in 2021 some reports that in some cases like Safari or IE/Edge do not return as expected.
    • toLocaleString() Work with numbers, Intl.NumberFormat Work with String/Numbers; Strings will be/have to be parsed and also rounded if necessary, so:
      • If we already have a localized string with non-English digits we have to replace numbers with the English one, then parse it, then use it again with the local options. (If it return what we expect)
      • Generally while parsing we cant expect not missing decimal zeros or details in big numbers or respecting other languages numeral characters
    • Decimal / Thousand separator characters can not be customized more than language options, except with post-fixings with replace() + RegEx again. (For example in Persian usually we don't use the suggested Arabic comma and also sometime we use Fraction/Division slash as decimal separator)
    • Slow performance in loops
  • Not so good RegEx ways (Fastest & One-liner ways)
    • /\B(?=(\d{3})+\b)/ it will group decimals too. // 123,123.123,123 !!!
    • /(?<!\.\d+)\B(?=(\d{3})+\b)/ used look-behind that not supported well yet. Please check:
      https://caniuse.com/js-regexp-lookbehind
      https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#browser_compatibility
      Note: Generally lookbehind can be against of original RegEx structure (because of how the analyzer should work like do not buffer the raw behind as a parser) and actually it can make the performance seriously low (In this case ~30%). I think it pushed inside over the time by requests.
    • /\B(?=(?=\d*\.)(\d{3})+(?!\d))/ just work with float numbers and ignore integers.
    • .replace(/(?:[^.\d]|^)\d+/g,function(a){return a.replace(/\B(?=(?:\d{3})+\b)/g,',');}) (My old idea) Using 2 RegEx. First one find the integer parts, second one put separator. Why 2 functions, when it can be mixed?
    • /(\..*)$|(\d)(?=(\d{3})+(?!\d))/g (Good idea by @djulien - i voted up) but when the RegEx is global, (\..*)$ it can make a mistake even with a space in end.
      Also using capturing groups (Example: (\d)) will make the performance low so if it possible, use non-capturing groups (Example: (?:\d)) or if an statement already exist in our function let's mix it.
      In this case, not using capturing groups improve performance about ~20% and in case of /\B(?=(\d{3})+\b)/g vs /\B(?=(?:\d{3})+\b)/g, the second one is ~8% faster.
      About regex performances: https://jsben.ch/HUmDA Note: Sure different methods, browsers, hardware, system status, cases and even changes on ECMAScript will effect on result of checking performance. But some changes logically should effect result and i used this one just as visual example.
  • Using library's like Numeral.js so much not necessary functions for a simple task.
  • Heavy code / Not accurate functions that used .split('.') or .toFixed() or Math.floor() ...

Final result:

There is no best of all and it should be chosen based on the need. My priority of sorting;

  1. Compatibility
  2. Capability
  3. Universality
  4. Ease of use
  5. Performance

toLocaleString() (Compatibility - Universality) [Native function]

  • If you have to change digits and grouping from English to another language
  • If you are not sure about your client language
  • If you don't need to have exact expected result
  • If you don't care about older version of Safari
// 1000000.2301
parseFloat(num) // (Pre-fix) If the input is string
    .toLocaleString('en-US', {
        useGrouping: true // (Default is true, here is just for show)
    });
// 1,000,000.23

Read more: https://www.w3schools.com/jsref/jsref_tolocalestring_number.asp

Intl.NumberFormat() (Capability - Universality - Compatibility) [Native function]

Almost same as toLocaleString() +

  • Great capability of supporting currency, units, etc... any language (Modern browsers)
// 1000000.2301
new Intl.NumberFormat('en-US', { // It can be 'fa-IR' : Farsi - Iran
    numberingSystem: 'arab'
}).format(num)
// ١٬٠٠٠٬٠٠٠٫٢٣

Read more: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat

With these much options of the native functions, we still can not expect:

  • Exact result (+ Not parsing the input / Not rounding / Not converting big numbers)
  • Accepting other languages digits as input
  • Customizing separators
  • Trusting browsers support
  • Performance

So you maybe need a function like any of these:

formatNums() (Compatibility - Ease of use)

Full version (Capability) (Not faster than toLocaleString) - Explain:

function formatNums(num, sep, dec, u) {
    // Setting defaults
    sep = sep || ','; // Seperator
    u = u || '\\d'; // Universal character set \d: 0-9 (English)
    // Mixing of Handeling numbers when the decimal character should be changed + Being sure the input is string
    if (typeof num != 'string') {
        num = String(num);
        if (dec && dec != '.') num = num.replace('.', dec); // Replacing sure decimal character with the custom
    }
    //
    return num.replace(RegExp('\\' + (dec || '.') + u + '+|' + u + '(?=(?:' + u + '{3})+(?!' + u + '))', 'g'),
        // The RegEx will be like /\.\d+|\d(?=(?:\d{3})+(?!\d))/g if not be customized 
        // RegEx explain:
        // 1) \.\d+  :  First try to get any part that started with a dot and followed by any much of English digits, one or more (For ignoring it later)
        // 2) |  :  Or
        // 3) \d  :  Get any 1 char digit
        // 3.1) (?=...)  :  That the next of that should be
        // 3.2) (?:\d{3})  :  3 length digits
        // 3.2.1) +  :  One or more of the group
        // 3.3) (?!\d)  :  ...till any place that there is no digits
        function(a) { // Any match can be the decimal part or the integer part so lets check it
            return a.length == 1 ? a + sep : a // If the match is one character, it is from the grouping part as item (3) in Regex explain so add the seperator next of it, if not, ignore it and return it back.
        })
}

function formatNums(num,sep,dec,u) {
    sep=sep||',';
    u=u||'\\d';
    if(typeof num!='string') {
        num=String(num);
        if( dec && dec!='.') num=num.replace('.',dec);
    }
    return num.replace(RegExp('\\'+(dec||'.')+u+'+|'+u+'(?=(?:'+u+'{3})+(?!'+u+'))','g'),function(a) {return a.length==1 ? a+sep : a})
}
console.log(formatNums(1000000.2301));
console.log(formatNums(100.2301));
console.log(formatNums(-2000.2301));
console.log(formatNums(123123123,' , '));
console.log(formatNums('0000.0000'));
console.log(formatNums('5000000.00'));
console.log(formatNums('5000000,00',' ',','));
console.log(formatNums(5000000.1234,' ',','));
console.log(formatNums('۱۲۳۴۵۶۷۸۹/۹۰۰۰',',','/','[\\d\\u0660-\\u0669\\u06f0-\\u06f9]'));

Play with the examples here: https://jsfiddle.net/PAPIONbit/198xL3te/

Light version (Performance) (~30% faster than toLocaleString)

function formatNums(num,sep) {
    sep=sep||',';
    return String(num).replace(/\.\d+|\d(?=(?:\d{3})+(?!\d))/g,
        function(a) {
            return a.length==1?a+sep:a
        }
    );
}
console.log(formatNums(1000000.2301));
console.log(formatNums(100.2301));
console.log(formatNums(-2000.2301));
console.log(formatNums(123123123,' '));

Check the RegEx (Without the necessary function) : https://regexr.com/66ott

(num+'').replace(/\B(?=(?:\d{3})+\b)/g,','); (Performance - Compatibility)

Best choose if The input is Specified / Predefined. (Like usual prices that sure will not have more than 3 decimals) (~65% faster than toLocaleString)

num=1000000;
str='123123.100';
console.log((num+'').replace(/\B(?=(?:\d{3})+\b)/g,','));
console.log(str.replace(/\B(?=(?:\d{3})+\b)/g,','));

+

For Persian/Arabic local clients:

If your client going to use Persian/Arabic numbers for input as what is usual in Iran, I think the best way is instead of keeping the original characters, convert them to English before you deal with, to you can calculate it.

// ۱۲۳۴۵۶۷۸۹۰
function toEnNum(n) { // Replacing Persian/Arabic numbers character with English
    n.replace(/[\u0660-\u0669\u06f0-\u06f9]/g, // RegEx unicode range Persian/Arabic numbers char
        function(c) {
            return c.charCodeAt(0) & 0xf; // Replace the char with real number by getting the binary index and breaking to lowest using 15
        }
    );
}
// 1234567890

And for still showing them as original looking there is 2 ways:


My Old-school function on this post: (~15% Faster than toLocalString)

// 10000000.0012
function formatNums(n, s) {
    return s = s || ",", String(n).
    replace(/(?:^|[^.\d])\d+/g, // First this RegEx take just integer parts
        function(n) {
            return n.replace(/\B(?=(?:\d{3})+\b)/g, s);
        })
}
// 10,000,000.0012
Restorative answered 27/9, 2019 at 14:24 Comment(2)
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.Calamite
@elias-zamaria check it out now by the new function, i removed negative behind look because not support in all browsersRestorative
V
11
var formatNumber = function (number) {
  var splitNum;
  number = Math.abs(number);
  number = number.toFixed(2);
  splitNum = number.split('.');
  splitNum[0] = splitNum[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  return splitNum.join(".");
}

EDIT: The function only work with positive number. for exmaple:

var number = -123123231232;
formatNumber(number)

Output: "123,123,231,232"

But to answer the question above toLocaleString() method just solves the problem.

var number = 123123231232;
    number.toLocaleString()

Output: "123,123,231,232"

Cheer!

Vesture answered 10/6, 2017 at 18:38 Comment(2)
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.Marsala
Nice script, but it does not work with negative numbers.Tacitus
V
9

My answer is the only answer that completely replaces jQuery with a much more sensible alternative:

function $(dollarAmount)
{
    const locale = 'en-US';
    const options = { style: 'currency', currency: 'USD' };
    return Intl.NumberFormat(locale, options).format(dollarAmount);
}

This solution not only adds commas, but it also rounds to the nearest penny in the event that you input an amount like $(1000.9999) you'll get $1,001.00. Additionally, the value you input can safely be a number or a string; it doesn't matter.

If you're dealing with money, but don't want a leading dollar sign shown on the amount, you can also add this function, which uses the previous function but removes the $:

function no$(dollarAmount)
{
    return $(dollarAmount).replace('$','');
}

If you're not dealing with money, and have varying decimal formatting requirements, here's a more versatile function:

function addCommas(number, minDecimalPlaces = 0, maxDecimalPlaces = Math.max(3,minDecimalPlaces))
{
    const options = {};
    options.maximumFractionDigits = maxDecimalPlaces;
    options.minimumFractionDigits = minDecimalPlaces;
    return Intl.NumberFormat('en-US',options).format(number);
}

Oh, and by the way, the fact that this code does not work in some old version of Internet Explorer is completely intentional. I try to break IE anytime that I can catch it not supporting modern standards.

Please remember that excessive praise, in the comment section, is considered off-topic. Instead, just shower me with up-votes.

Volatilize answered 8/8, 2019 at 5:15 Comment(2)
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.Undesigning
@Undesigning : 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.Volatilize
A
8

I Wrote this one before stumbling on this post. No regex and you can actually understand the code.

$(function(){
  
  function insertCommas(s) {

    // get stuff before the dot
    var d = s.indexOf('.');
    var s2 = d === -1 ? s : s.slice(0, d);

    // insert commas every 3 digits from the right
    for (var i = s2.length - 3; i > 0; i -= 3)
      s2 = s2.slice(0, i) + ',' + s2.slice(i);

    // append fractional part
    if (d !== -1)
      s2 += s.slice(d);

    return s2;

  }
  
  
  $('#theDudeAbides').text( insertCommas('1234567.89012' ) );
  
  
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id="theDudeAbides"></div>
Appreciate answered 25/11, 2014 at 21:41 Comment(2)
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.Hepler
only works if you pass it a string.. an easy fix but just wanted to note itTanana
S
8

Here's a simple reusable function that returns a string with the specified number of decimal places and lets you toggle the inclusion of a comma.

function format_number(number, num_decimals, include_comma)
{
    return number.toLocaleString('en-US', {useGrouping: include_comma, minimumFractionDigits: num_decimals, maximumFractionDigits: num_decimals});
}

Usage examples:

format_number(1234.56789, 2, true); // Returns '1,234.57'
format_number(9001.42, 0, false); // Returns '9001'

If you need to customize the string further, you can find the list of formatting options here.

Specialist answered 23/8, 2022 at 10:54 Comment(0)
R
7

For anyone who likes 1-liners and a single regex, but doesn't want to use split(), here is an enhanced version of the regex from other answers that handles (ignores) decimal places:

    var formatted = (x+'').replace(/(\..*)$|(\d)(?=(\d{3})+(?!\d))/g, (digit, fract) => fract || digit + ',');

The regex first matches a substring starting with a literal "." and replaces it with itself ("fract"), and then matches any digit followed by multiples of 3 digits and puts "," after it.

For example, x = 12345678.12345678 will give formatted = '12,345,678.12345678'.

Relay answered 18/11, 2019 at 0:41 Comment(0)
A
6

An alternative way, supporting decimals, different separators and negatives.

var number_format = function(number, decimal_pos, decimal_sep, thousand_sep) {
    var ts      = ( thousand_sep == null ? ',' : thousand_sep )
        , ds    = ( decimal_sep  == null ? '.' : decimal_sep )
        , dp    = ( decimal_pos  == null ? 2   : decimal_pos )

        , n     = Math.floor(Math.abs(number)).toString()

        , i     = n.length % 3 
        , f     = ((number < 0) ? '-' : '') + n.substr(0, i)
    ;

    for(;i<n.length;i+=3) {
        if(i!=0) f+=ts;
        f+=n.substr(i,3);
    }

    if(dp > 0) 
        f += ds + parseFloat(number).toFixed(dp).split('.')[1]

    return f;
}

Some corrections by @Jignesh Sanghani, don't forget to upvote his comment.

Anele answered 14/7, 2014 at 5:50 Comment(6)
Perfect for me, just added a new line to remove formatting before processing.Cataldo
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 codeDisrobe
flawed. number grows. an exampel call would have been great!Ovarian
switching ceil to floor fixed that but unsure what other issues will arise.Ovarian
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.Ovarian
Try Math.floor(-75.1) ;)Ovarian
M
6

Let me try to improve uKolka's answer and maybe help others save some time.

Use Numeral.js.

document.body.textContent = numeral(1234567).format('0,0');
<script src="//cdnjs.cloudflare.com/ajax/libs/numeral.js/1.4.5/numeral.min.js"></script>

You should go with Number.prototype.toLocaleString() only if its browser compatibilty is not an issue.

Markham answered 5/2, 2015 at 15:44 Comment(1)
this inspired me to npm install numeralDonohoe
R
6

Just for future Googlers (or not necessarily 'Googlers'):

All of solutions mentioned above are wonderful, however, RegExp might be awfully bad thing to use in a situation like that.

So, yes, you might use some of the options proposed or even write something primitive yet useful like:

const strToNum = str => {

   //Find 1-3 digits followed by exactly 3 digits & a comma or end of string
   let regx = /(\d{1,3})(\d{3}(?:,|$))/;
   let currStr;

   do {
       currStr = (currStr || str.split(`.`)[0])
           .replace( regx, `$1,$2`)
   } while (currStr.match(regx)) //Stop when there's no match & null's returned

   return ( str.split(`.`)[1] ) ?
           currStr.concat(`.`, str.split(`.`)[1]) :
           currStr;

};

strToNum(`123`) // => 123
strToNum(`123456`) // => 123,456
strToNum(`-1234567.0987`) // => -1,234,567.0987

The regexp that's used here is fairly simple and the loop will go precisely the number of times it takes to get the job done.

And you might optimize it far better, "DRYify" code & so on.

Yet,

(-1234567.0987).toLocaleString();

(in most situations) would be a far better choice.

The point is not in the speed of execution or in cross-browser compatibility.

In situations when you'd like to show the resulting number to user, .toLocaleString() method gives you superpower to speak the same language with the user of your website or app (whatever her/his language is).

This method according to ECMAScript documentation was introduced in 1999, and I believe that the reason for that was the hope that the Internet at some point will connect people all around the world, so, some "internalization" tools were needed.

Today the Internet does connect all of us, so, it is important to remember that the world is a way more complex that we might imagine & that (/almost) all of us are here, in the Internet.

Obviously, considering the diversity of people, it is impossible to guarantee perfect UX for everybody because we speak different languages, value different things, etc. And exactly because of this, it is even more important to try to localize things as much as it's possible.

So, considering that there're some particular standards for representation of date, time, numbers, etc. & that we have a tool to display those things in the format preferred by the final user, isn't that rare and almost irresponsible not to use that tool (especially in situations when we want to display this data to the user)?

For me, using RegExp instead of .toLocaleString() in situation like that sounds a little bit like creating a clock app with JavaScript & hard-coding it in such a way so it'll display Prague time only (which would be quite useless for people who don't live in Prague) even though the default behaviour of

new Date();

is to return the data according to final user's clock.

Ruelu answered 15/3, 2019 at 0:47 Comment(3)
why did you write the function with const and => ?Fraught
@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.Ruelu
This is the right answer, people are wildly overcomplicating this. toLocaleString() is built for this exact use case.Zee
F
5

I think your solution is one of the shorter ones I've seen for this. I don't think there are any standard JavaScript functions to do this sort of thing, so you're probably on your own.

I checked the CSS 3 specifications to see whether it's possible to do this in CSS, but unless you want every digit in its own <span>, I don't think that's possible.

I did find one project on Google Code that looked promising: flexible-js-formatting. I haven't used it, but it looks pretty flexible and has unit tests using JsUnit. The developer also has a lot of posts (though old) about this topic.

Be sure to consider international users: lots of nations use a space as the separator and use the comma for separating the decimal from the integral part of the number.

Floozy answered 24/5, 2010 at 23:58 Comment(1)
Could you post the solution using CSS only & spans?Skilken
V
5

I think this function will take care of all the issues related to this problem.

function commaFormat(inputString) {
    inputString = inputString.toString();
    var decimalPart = "";
    if (inputString.indexOf('.') != -1) {
        //alert("decimal number");
        inputString = inputString.split(".");
        decimalPart = "." + inputString[1];
        inputString = inputString[0];
        //alert(inputString);
        //alert(decimalPart);

    }
    var outputString = "";
    var count = 0;
    for (var i = inputString.length - 1; i >= 0 && inputString.charAt(i) != '-'; i--) {
        //alert("inside for" + inputString.charAt(i) + "and count=" + count + " and outputString=" + outputString);
        if (count == 3) {
            outputString += ",";
            count = 0;
        }
        outputString += inputString.charAt(i);
        count++;
    }
    if (inputString.charAt(0) == '-') {
        outputString += "-";
    }
    //alert(outputString);
    //alert(outputString.split("").reverse().join(""));
    return outputString.split("").reverse().join("") + decimalPart;
}
Vermont answered 19/11, 2012 at 13:17 Comment(0)
L
5

Lots of good answers already. Here's another, just for fun:

function format(num, fix) {
    var p = num.toFixed(fix).split(".");
    return p[0].split("").reduceRight(function(acc, num, i, orig) {
        if ("-" === num && 0 === i) {
            return num + acc;
        }
        var pos = orig.length - i - 1
        return  num + (pos && !(pos % 3) ? "," : "") + acc;
    }, "") + (p[1] ? "." + p[1] : "");
}

Some examples:

format(77.03453, 2); // "77.03"
format(78436589374); // "78,436,589,374"
format(784, 4);      // "784.0000"
format(-123456);     // "-123,456"
Laurentia answered 8/1, 2014 at 19:13 Comment(2)
this doesn't work with format(-123456) it gives -,123,456Blackfoot
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.Laurentia
F
5

If you're looking for a short and sweet solution:

const number = 12345678.99;

const numberString = String(number).replace(
    /^\d+/,
    number => [...number].map(
        (digit, index, digits) => (
            !index || (digits.length - index) % 3 ? '' : ','
        ) + digit
    ).join('')
);

// numberString: 12,345,678.99
Five answered 17/12, 2020 at 17:21 Comment(4)
That is telling me "Uncaught TypeError: number is not iterable". Maybe you need to call toString on the number.Calamite
@EliasZamaria sorry, I used a string in my case. Updated my answer to convert to string.Five
I tried 12.34 as the number and it returned 12,.34.Calamite
I thought it was only meant to work with decimals. Updated for you.Five
A
4

My true regular-expressions-only solution for those love one-liners

You see those enthusiastic players above? Maybe you can golf out of it. Here’s my stroke.

n => `${n}`.replace(/(?<!\.\d+)\B(?=(\d{3})+\b)/g, " ").replace(/(?<=\.(\d{3})+)\B/g, " ")

Uses a THIN SPACE (U+2009) for a thousands separator, as the International System of Units said to do in the eighth edition(2006) of their publication “SI Brochure: The International System of Units (SI) (See §5.3.4.). The ninth edition(2019) suggests to use a space for it (See §5.4.4.). You can use whatever you want, including a comma.


See.

const integer_part_only = n => `${n}`.replace(/(?<!\.\d+)\B(?=(\d{3})+\b)/g, " I ");
const fractional_part_only = n => `${n}`.replace(/(?<=\.(\d{3})+)\B/g, " F ");
const both = n => fractional_part_only(integer_part_only(n));

function demo(number) { // I’m using Chrome 74.
	console.log(`${number}
		→ "${integer_part_only(number)}" (integer part only)
		→ "${fractional_part_only(number)}" (fractional part only)
		→ "${both(number)}" (both)
	`);
}
demo(Math.random() * 10e5);
demo(123456789.01234567);
demo(123456789);
demo(0.0123456789);

How does it work?

For an integer part

.replace(/(?<!\.\d+)\B(?=(\d{3})+\b)/g, " I ")
  • .replace(……, " I ") Put “ I ”
    • /……/g at each of
      • \B the in-between of two adjacent digits
        • (?=……)POSITIVE LOOKAHEAD whose right part is
          • (\d{3})+ one or more three-digit chunks
          • \b followed by a non-digit, such as, a period, the ending of the string, et cetera,
        • (?<!……)NEGATIVE LOOKBEHIND excluding ones whose left part
          • \.\d+ is a dot followed by digits (“has a decimal separator”).

For a decimal part

.replace(/(?<=\.(\d{3})+)\B/g, " F ")
  • .replace(……, " F ") Put “ F ”
    • /……/g at each of
      • \B the in-between of two adjacent digits
        • (?<=……)POSITIVE LOOKBEHIND whose left part is
          • \. a decimal separator
          • (\d{3})+ followed by one or more three-digit chunks.

Character classes and boundaries

\d

Matches any digit (Arabic numeral). Equivalent to [0-9].

For example,

  • /\d/ or /[0-9]/ matches 2 in B2 is the suite number.

\b

Matches a word boundary. This is the position where a word character is not followed or preceded by another word-character, such as between a letter and a space. Note that a matched word boundary is not included in the match. In other words, the length of a matched word boundary is zero.

Examples:

  • /\bm/ matches the m in moon ;
  • /oo\b/ does not match the oo in moon, because oo is followed by n which is a word character;
  • /oon\b/ matches the oon in moon, because oon is the end of the string, thus not followed by a word character;
  • /\w\b\w/ will never match anything, because a word character can never be followed by both a non-word and a word character.

\B

Matches a non-word boundary. This is a position where the previous and next character are of the same type: either both must be words, or both must be non-words. Such as between two letters or between two spaces. The beginning and end of a string are considered non-words. Same as the matched word boundary, the matched non-word boundary is also not included in the match.

For example,

  • /\Bon/ matches on in at noon;
  • /ye\B/ matches ye in possibly yesterday.

Browser compatibility

Adon answered 14/6, 2019 at 10:50 Comment(0)
W
4

var floatingNumber = 1000000.06665656676709;
var doubleNumber = floatingNumber.toFixed(2);
var athousand = parseFloat(doubleNumber).toLocaleString();
document.write(athousand);
Withers answered 22/7, 2023 at 14:25 Comment(0)
C
3

I added tofixed to Aki143S's solution. This solution uses dots for thousands separators and comma for the precision.

function formatNumber( num, fixed ) { 
    var decimalPart;

    var array = Math.floor(num).toString().split('');
    var index = -3; 
    while ( array.length + index > 0 ) { 
        array.splice( index, 0, '.' );              
        index -= 4;
    }

    if(fixed > 0){
        decimalPart = num.toFixed(fixed).split(".")[1];
        return array.join('') + "," + decimalPart; 
    }
    return array.join(''); 
};

Examples;

formatNumber(17347, 0)  = 17.347
formatNumber(17347, 3)  = 17.347,000
formatNumber(1234563.4545, 3)  = 1.234.563,454
Cuticula answered 26/9, 2012 at 14:43 Comment(0)
I
3

Related to @elias-zamaria and @t.j.crowder

A negative lookbehind for Safari browser is can't use <. So, it would be (?!\.\d*)

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

It works for Safari and Firefox

Indult answered 23/3, 2022 at 17:47 Comment(0)
G
2

The solution from @user1437663 is great.

Who really understands the solution is being prepared to understand complex regular expressions.

A small improvement to make it more readable:

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

The pattern starts with \B to avoid use comma at the beginning of a word. Interestingly, the pattern is returned empty because \B does not advance the "cursor" (the same applies to $).

O \B is followed by a less known resources but is a powerful feature from Perl's regular expressions.

            Pattern1 (? = (Pattern2) ).

The magic is that what is in parentheses (Pattern2) is a pattern that follows the previous pattern (Pattern1) but without advancing the cursor and also is not part of the pattern returned. It is a kind of future pattern. This is similar when someone looks forward but really doesn't walk!

In this case pattern2 is

\d{3})+(?=$)

It means 3 digits (one or more times) followed by the end of the string ($)

Finally, Replace method changes all occurrences of the pattern found (empty string) for comma. This only happens in cases where the remaining piece is a multiple of 3 digits (such cases where future cursor reach the end of the origin).

Gerena answered 12/1, 2013 at 20:17 Comment(0)
D
2

After not finding a modern and comprehensive solution here, I have written an arrow function (without regex) to solve the formatting problem and it allows the caller to provide number of fraction digits as well as the period and thousand separator for Europe and rest of the world.

Examples:

numberFormatter(1234567890.123456) => 1,234,567,890
numberFormatter(1234567890.123456, 4) => 1,234,567,890.1235
numberFormatter(1234567890.123456, 4, '.', ',') => 1.234.567.890,1235 Europe

Here is the function written in ES6 (modern syntax):

const numberFormatter = (number, fractionDigits = 0, thousandSeperator = ',', fractionSeperator = '.') => {
    if (number!==0 && !number || !Number.isFinite(number)) return number
    const frDigits = Number.isFinite(fractionDigits)? Math.min(Math.max(fractionDigits, 0), 7) : 0
    const num = number.toFixed(frDigits).toString()

    const parts = num.split('.')
    let digits = parts[0].split('').reverse()
    let sign = ''
    if (num < 0) {sign = digits.pop()}
    let final = []
    let pos = 0

    while (digits.length > 1) {
        final.push(digits.shift())
        pos++
        if (pos % 3 === 0) {final.push(thousandSeperator)}
    }
    final.push(digits.shift())
    return `${sign}${final.reverse().join('')}${frDigits > 0 ? fractionSeperator : ''}${frDigits > 0 && parts[1] ? parts[1] : ''}`
}

It has been tested for negative, bad input and NaN cases. If the input is NaN then it simply returns it.

Demarche answered 13/5, 2018 at 23:43 Comment(0)
A
2

var number = 1234567;
var formattedNumber = number.toLocaleString();

console.log(formattedNumber); // Output: 1,234,567
If you want to force a specific locale or customize the formatting options, you can pass them as arguments to the toLocaleString method.

var number = 1234567;
var formattedNumber = number.toLocaleString('en-US', { style: 'decimal' });

console.log(formattedNumber); // Output: 1,234,567
Automatic answered 12/12, 2023 at 19:12 Comment(0)
G
1

Here is good solution with less coding...

var y = "";
var arr = x.toString().split("");
for(var i=0; i<arr.length; i++)
{
    y += arr[i];
    if((arr.length-i-1)%3==0 && i<arr.length-1) y += ",";
}
Gasbag answered 12/3, 2014 at 6:8 Comment(6)
this doesn't work for -123456 it gives -1,234,56Blackfoot
that still doesn't work when the input is -123456. it gives -,123,456 jsfiddle.net/wrossmck/2R8mD/1Blackfoot
this is just crazy :)Wane
Haha I agree with petr its fun to look at though.Tweed
this allows characters like abcdef and so on.. which should be restrictedAngellaangelle
You could also loop through x.toString().length and use x[i]Ansilma
M
1

I've adapted your code to work in TextBox (Input type="text") so we can enter and delete digits in real time without losing cursor. It's works also if you select range when you delete. And you can use arrows and home/end buttons freely.
Thanks for saving my time!

//function controls number format as "1,532,162.3264321"
function numberWithCommas(x) {
    var e = e || window.event;
    if (e.keyCode >= '35' && e.keyCode <= '40') return; //skip arrow-keys
    var selStart = x.selectionStart, selEnd = x.selectionEnd; //save cursor positions
    var parts = x.value.toString().split(".");
    var part0len = parts[0].length; //old length to check if new ',' would be added. Need for correcting new cursor position (+1 to right).

    //if user deleted ',' - remove previous number instead (without selection)
    if (x.selectionLength == 0 && (e.keyCode == 8 || e.keyCode == 46)) {//if pressed 8-backspace or 46-delete button
        var delPos = parts[0].search(/\d{4}/);
        if (delPos != -1) {//if found 4 digits in a row (',' is deleted)
            if (e.keyCode == 8) {//if backspace flag
                parts[0] = parts[0].slice(0, selStart - 1) + parts[0].slice(selEnd, parts[0].length);
                selEnd--;
                if (selStart > selEnd) selStart = selEnd;
            } else {
                parts[0] = parts[0].slice(0, selStart) + parts[0].slice(selEnd + 1, parts[0].length);
                selStart++;
                if (selEnd < selStart) selEnd = selStart;
            }
        }
    }

   var hasMinus = parts[0][0] == '-';
   parts[0] = (hasMinus ? '-' : '') + parts[0].replace(/[^\d]*/g, ""); //I'd like to clear old ',' to avoid things like 1,2,3,5,634.443216
   parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); //sets ',' between each 3 digits
   if (part0len < parts[0].length) { //move cursor to right if added new ','
       selStart++;
       selEnd++;
   } else if (part0len > parts[0].length) { //..or if removed last one ','
       selStart--;
       selEnd--;
   }
   x.value = parts.join(".");
   x.setSelectionRange(selStart, selEnd); //restoring cursor position
}
function saveSelectionLength(x) {
    x.selectionLength = x.selectionEnd - x.selectionStart;
}

To use this just added two events - onKeyUp and onKeyDown

<asp:TextBox runat="server" ID="val" Width="180px" onKeyUp="numberWithCommas(this);" onKeyDown="saveSelectionLength(this);"/>
Mozza answered 23/3, 2015 at 16:29 Comment(0)
S
1

I've found an approach that works in every situation. CodeSandbox example

function commas(n) {
  if (n < 1000) {
    return n + ''
  } else {
    // Convert to string.
    n += ''

    // Skip scientific notation.
    if (n.indexOf('e') !== -1) {
      return n
    }

    // Support fractions.
    let i = n.indexOf('.')
    let f = i == -1 ? '' : n.slice(i)
    if (f) n = n.slice(0, i)

    // Add commas.
    i = n.length
    n = n.split('')
    while (i > 3) n.splice((i -= 3), 0, ',')
    return n.join('') + f
  }
}

This is like Noah Freitas' answer, but with support for fractions and scientific notation.

I think toLocaleString is the best choice, if performance is not a concern.

edit: Here's a CodeSandbox with some examples: https://codesandbox.io/s/zmvxjpj6x

Sharper answered 30/5, 2018 at 19:25 Comment(2)
I tried commas(12345.67) and got "12,345.6700000000000728".Calamite
@EliasZamaria whoops! forgot about imprecise arithmetic. fixed :)Sharper
A
1
let formatNumber = (number) => {
    let str = String(number)

    return str.split('').reduce(
        (a, b, i) => a + (i && !((str.length - i) % 3) ? ',' : '') + b,
        ''
    )
}
Ansilma answered 22/4, 2021 at 6:12 Comment(2)
This is the simplest solution without regexp I ever saw.Beatify
@Beatify it is very concise but not perf optimal. thank you for bringing that up for me i took more time to play with it and found even performance differences between node, deno and bun. here: https://mcmap.net/q/40470/-how-to-format-a-number-with-commas-as-thousands-separatorsAnsilma
B
0

Yet another..(for int's as the question asks)

function insertCommas(str)
{
    var a = str.split("");
    a.reverse();

    var t, i = 0, arr = Array();

    while (t = a.shift())
    {
       if (((i++ % 3) == 0) && arr.length > 0)
           arr.unshift(",");
       arr.unshift(t);
    }

    return arr.join("");
}
Beater answered 19/11, 2013 at 2:4 Comment(0)
S
0

I thought I'd share a little trick which I'm using for large number formatting. Instead of inserting commas or spaces, I insert an empty but visible span in between the "thousands". This makes thousands easily visible, but it allows to copy/paste the input in the original format, without commas/spaces.

// This function accepts an integer, and produces a piece of HTML that shows it nicely with 
// some empty space at "thousand" markers. 
// Note, these space are not spaces, if you copy paste, they will not be visible.
function valPrettyPrint(orgVal) {
  // Save after-comma text, if present
  var period = orgVal.indexOf(".");
  var frac = period >= 0 ? orgVal.substr(period) : "";
  // Work on input as an integer
  var val = "" + Math.trunc(orgVal);
  var res = "";
  while (val.length > 0) {
    res = val.substr(Math.max(0, val.length - 3), 3) + res;
    val = val.substr(0, val.length - 3);
    if (val.length > 0) {
        res = "<span class='thousandsSeparator'></span>" + res;
    }
  }
  // Add the saved after-period information
  res += frac;
  return res;
}

With this CSS:

.thousandsSeparator {
  display : inline;
  padding-left : 4px;
}

See an example JSFiddle.

Sanctity answered 28/3, 2016 at 5:40 Comment(3)
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/ze6agw7vDished
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.Sanctity
I have taken this a step further by displaying un-selectable commas: jsfiddle.net/ovfd83py/13Cindycine
F
0

If you happen to be using AngularJS, there's this currency filter that may definitely help: http://www.w3schools.com/angular/ng_filter_currency.asp

Fremitus answered 6/1, 2017 at 21:51 Comment(0)
F
0

Here's my try:

EDIT: Added in decimals

function splitMille(n, separator = ',') {
  // Cast to string
  let num = (n + '')

  // Test for and get any decimals (the later operations won't support them)
  let decimals = ''
  if (/\./.test(num)) {
    // This regex grabs the decimal point as well as the decimal numbers
    decimals = num.replace(/^.*(\..*)$/, '$1')
  }
  
  // Remove decimals from the number string
  num = num.replace(decimals, '')
    // Reverse the number string through Array functions
    .split('').reverse().join('')
    // Split into groups of 1-3 characters (with optional supported character "-" for negative numbers)
    .match(/[0-9]{1,3}-?/g)
    // Add in the mille separator character and reverse back
    .join(separator).split('').reverse().join('')

  // Put the decimals back and output the formatted number
  return `${num}${decimals}`
}

let testA = splitMille(1234)
let testB = splitMille(-1234)
let testC = splitMille(123456.789)
let testD = splitMille(9007199254740991)
let testE = splitMille(1000.0001)

console.log('Results!\n\tA: %s\n\tB: %s\n\tC: %s\n\tD: %s\n\tE: %s', testA, testB, testC, testD, testE)
Fibro answered 28/6, 2017 at 15:23 Comment(0)
P
0

Here is a one line function with int & decimal support. I left some code in to convert the number to a string as well.

    function numberWithCommas(x) {
        return (x=x+'').replace(new RegExp('\\B(?=(\\d{3})+'+(~x.indexOf('.')?'\\.':'$')+')','g'),',');
    }
Polystyrene answered 22/9, 2017 at 18:27 Comment(0)
M
0

You can create a function on the Number prototype

Number.prototype.format = function (s, d) {
  return (
    this.toString()
      .split(".")
      .map((n, i) =>
        i
          ? n
          : n
              .split("")
              .map((n, i) => (i % 3 || !i ? n : s + n))
              .join("")
      )
      .join(d)
  );
};

console.log((8800.00).format(',', '.'))
// 8,880.00

// French notation
console.log((8800.00).format(' ', ','))
// 8 880,00
Mahogany answered 10/11, 2020 at 1:35 Comment(0)
A
0

Another approach with complexity of n(Math.floor(str.length / 3)) instead of n(str.length)

function formatNumber(num) {
  let str = String(num)
  
  if (num < 1000) return str

  let result = ''

  for (let i = Math.floor(str.length / 3); i >= 0; i--) {
    let start = str.length - 3 * (i + 1)
    let end = start + 3

    result +=
      (end ? str.slice(Math.max(0, start), end) : '') + (end && i ? ',' : '')
  }

  return result
}
Ansilma answered 27/9, 2022 at 2:48 Comment(4)
It is unclear to me what advantage this has over the other answers. Also, I ran formatNumber(6543.21) and got 6,543,.21, which looks kind of strange with that extra comma.Calamite
@EliasZamaria i did not cover the decimal part. Instead of going char by char, you lower the number of iterations to / 3Ansilma
Less loop is effective but this version is using for less effective than reduce, and using slice and max function for each loop. I did not compare this version and old version but I think your old version is more effective in real world because most numbers we are using is less than million.Beatify
I added another solution that increases 3 in each step based on your idea. Thanks. https://mcmap.net/q/40470/-how-to-format-a-number-with-commas-as-thousands-separatorsBeatify
B
0

I challenge with another solution.

First code was this.

function formatNumber(number) {
  if (number < 1000) {
    return String(number);
  }
  if (number < 1000000) {
    let numbers = String(number).split('');
    numbers.splice(-3, 0, ',');
    return numbers.join('');
  }
  if (number < 1000000000) {
    let numbers = String(number).split('');
    numbers.splice(-3, 0, ',');
    numbers.splice(-7, 0, ',');
    return numbers.join('');
  }
  if (number < 1000000000000) {
    let numbers = String(number).split('');
    numbers.splice(-3, 0, ',');
    numbers.splice(-7, 0, ',');
    numbers.splice(-11, 0, ',');
    return numbers.join('');
  }

  throw new Error(`number: ${number} is too big`);
}

This code is lengthy and not scalable, but it is effective because it checks number itself not length of string that was converted from number.

So I converted to neat code based on this idea. (You can make 12 more bigger to prevent error.)

function formatNumber(number) {
  let commas = -1;
  for (let n3 = 3; n3 <= 12; n3 += 3) {
    commas++;
    const max = Math.pow(10, n3);
    if (number < max) {
      let numbers = String(number).split('');
      for (let i = 0; i < commas; i++) {
        numbers.splice(-(3 * (i + 1) + i), 0, ',');
      }
      return numbers.join('');
    }
  }

  throw new Error(`number: ${number} is too big`);
}
Beatify answered 28/9, 2022 at 15:21 Comment(0)
E
-1

I have found this answer somewhere earlier , and I updated it to allow negative numbers.

You can use it after converting the number to string.

The removal of the additional decimal places is just for convenience as it is a very common situation.You can skip it if not needed.

// Keep only digits, hyphen  and decimal points:
             myNum.toString()   .replace(/[^-\d.]/g, "")
                // Remove duplicated decimal point, if one exists:
                .replace(/^(\d*\.)(.*)\.(.*)$/, '$1$2$3')
                // Keep only two digits past the decimal point:
                .replace(/\.(\d{2})\d+/, '.$1')
                // Add thousands separators:
                .replace(/\B(?=(\d{3})+(?!\d))/g, ",")
Ensemble answered 19/9, 2022 at 21:20 Comment(3)
OK... this is not at all what I asked for. This must only work with strings, since numbers don't have a replace method. And removing duplicated decimal points and keeping only 2 digits after the decimal point is irrelevant to my question.Calamite
Perhaps you could update the answer based on Elias' feedback?Preordain
that last .replace does the job for me, nice, thanks !Afoul

© 2022 - 2024 — McMap. All rights reserved.