57

I have a function to add commas to numbers:

function commafy( num ) {
  num.toString().replace( /\B(?=(?:\d{3})+)$/g, "," );
}

Unfortunately, it doesn't like decimals very well. Given the following usage examples, what is the best way to extend my function?

commafy( "123" )                 // "123"
commafy( "1234" )                // "1234"
                                 // Don't add commas until 5 integer digits
commafy( "12345" )               // "12,345"
commafy( "1234567" )             // "1,234,567"
commafy( "12345.2" )             // "12,345.2"
commafy( "12345.6789" )          // "12,345.6789"
                                 // Again, nothing until 5
commafy( ".123456" )             // ".123 456"
                                 // Group with spaces (no leading digit)
commafy( "12345.6789012345678" ) // "12,345.678 901 234 567 8"

Presumably the easiest way is to first split on the decimal point (if there is one). Where best to go from there?

Sophie Alpert
  • 139,698
  • 36
  • 220
  • 238
  • Similar question & answer here: http://stackoverflow.com/questions/6614268/how-can-i-add-a-comma-to-separate-each-group-of-three-digits-in-a-text-input-fie/24261626#24261626 – az3 Jun 17 '14 at 10:40
  • Is there a way to make the output format in FORTRAN like this, without writing any new functions ? – Nike Jul 08 '15 at 14:38
  • Reminder that the Japanese like their numbers in groups of 4, while Indians prefer a final 3-group preceded by 2-groups. Gross oversimplification here, mind you. – badp Jan 24 '17 at 19:41

14 Answers14

99

Just split into two parts with '.' and format them individually.

function commafy( num ) {
    var str = num.toString().split('.');
    if (str[0].length >= 5) {
        str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1,');
    }
    if (str[1] && str[1].length >= 5) {
        str[1] = str[1].replace(/(\d{3})/g, '$1 ');
    }
    return str.join('.');
}
Ghostoy
  • 2,689
  • 19
  • 18
  • I did a web service, based on that http://valjok.blogspot.com/2014/07/format-number-with-thouthands-separator.html – Val Jul 22 '14 at 12:27
65

Simple as that:

var theNumber = 3500;
theNumber.toLocaleString();
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
Itay Merchav
  • 954
  • 8
  • 8
  • `toLocaleString()` cuts down the precision of the number to 3 decimal places on my machine.. this may not be what you want.. – AweSIM Jun 26 '17 at 21:29
  • 8
    toLocaleString can use options. You may want to use "maximumFractionDigits" can be in a range of 1 - 21. To see the full description https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString – Itay Merchav Oct 07 '17 at 04:26
21

Here are two concise ways I think maybe useful:

  1. Number.prototype.toLocaleString

This method can convert a number to a string with a language-sensitive representation. It allows two parameters, which is locales & options. Those parameters may be a bit confusing, for more detail see that doc from MDN above.

In a word, you could simply use is as below:

console.log(
   Number(1234567890.12).toLocaleString()
)
// log -> "1,234,567,890.12"

If you see different with me that because we ignore both two parameters and it will return a string base on your operation system.

  1. Use regex to match a string then replace to a new string.

    Why we consider this? The toLocaleString() is a bit confusing and not all browser supported, also toLocaleString() will round the decimal, so we can do it in another way.

// The steps we follow are:
// 1. Converts a number(integer) to a string.
// 2. Reverses the string.
// 3. Replace the reversed string to a new string with the Regex
// 4. Reverses the new string to get what we want.

// This method is use to reverse a string.
function reverseString(str) { 
    return str.split("").reverse().join("");  
}

/**
 * @param {string | number} 
 */
function groupDigital(num) {
  const emptyStr = '';
  const group_regex = /\d{3}/g;

  // delete extra comma by regex replace.
  const trimComma = str => str.replace(/^[,]+|[,]+$/g, emptyStr)


  const str = num + emptyStr;
  const [integer, decimal] = str.split('.')

  const conversed = reverseString(integer);

  const grouped = trimComma(reverseString(
    conversed.replace(/\d{3}/g, match => `${match},`)
  ));

  return !decimal ? grouped : `${grouped}.${decimal}`;
}


console.log(groupDigital(1234567890.1234)) // 1,234,567,890.1234
console.log(groupDigital(123456))  // 123,456
console.log(groupDigital("12.000000001"))  // 12.000000001

TommY
  • 738
  • 6
  • 14
  • 1
    Tips: 1. You can extend the second way to be more flexible and applicable. 2. To avoid truncated you should use string prefer than a number as input. – TommY Jul 22 '19 at 07:17
9

Easiest way:

1

var num = 1234567890,
result = num.toLocaleString() ;// result will equal to "1 234 567 890"

2

var num = 1234567.890,
result = num.toLocaleString() + num.toString().slice(num.toString().indexOf('.')) // will equal to 1 234 567.890

3

var num = 1234567.890123,
result = Number(num.toFixed(0)).toLocaleString() + '.' + Number(num.toString().slice(num.toString().indexOf('.')+1)).toLocaleString()
//will equal to 1 234 567.890 123

4

If you want ',' instead of ' ':

var num = 1234567.890123,
result = Number(num.toFixed(0)).toLocaleString().split(/\s/).join(',') + '.' + Number(num.toString().slice(num.toString().indexOf('.')+1)).toLocaleString()
//will equal to 1,234,567.890 123

If not working, set the parameter like: "toLocaleString('ru-RU')" parameter "en-EN", will split number by the ',' instead of ' '

All function used in my code are native JS functions. You'll find them in GOOGLE or in any JS Tutorial/Book

2

I have extended #RobG's answer a bit more and made a sample jsfiddle

function formatNum(n, prec, currSign) {
    if(prec==null) prec=2;
  var n = ('' + parseFloat(n).toFixed(prec).toString()).split('.');
  var num = n[0];
  var dec = n[1];
  var r, s, t;

  if (num.length > 3) {
    s = num.length % 3;

    if (s) {
      t = num.substring(0,s);
      num = t + num.substring(s).replace(/(\d{3})/g, ",$1");
    } else {
      num = num.substring(s).replace(/(\d{3})/g, ",$1").substring(1);
    }
  }
    return (currSign == null ? "": currSign +" ") + num + (dec? '.' + dec : '');
}
alert(formatNum(123545.3434));
alert(formatNum(123545.3434,2));
alert(formatNum(123545.3434,2,'€'));

and extended same way the #Ghostoy's answer

function commafy( num, prec, currSign ) {
    if(prec==null) prec=2;
    var str = parseFloat(num).toFixed(prec).toString().split('.');
    if (str[0].length >= 5) {
        str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1,');
    }
    if (str[1] && str[1].length >= 5) {
        str[1] = str[1].replace(/(\d{3})/g, '$1 ');
    }
    return (currSign == null ? "": currSign +" ") + str.join('.');
}

alert(commafy(123545.3434));
Kristijan Iliev
  • 4,901
  • 10
  • 28
  • 47
HGMamaci
  • 1,339
  • 12
  • 20
2

If you are happy with the integer part (I haven't looked at it closly), then:

function formatDecimal(n) {
  n = n.split('.');
  return commafy(n[0]) + '.' + n[1];
}

Of course you may want to do some testing of n first to make sure it's ok, but that's the logic of it.

Edit

Ooops! missed the bit about spaces! You can use the same regular exprssion as commafy except with spaces instead of commas, then reverse the result.

Here's a function based on vol7ron's and not using reverse:

function formatNum(n) {
  var n = ('' + n).split('.');
  var num = n[0];
  var dec = n[1];
  var r, s, t;

  if (num.length > 3) {
    s = num.length % 3;

    if (s) {
      t = num.substring(0,s);
      num = t + num.substring(s).replace(/(\d{3})/g, ",$1");
    } else {
      num = num.substring(s).replace(/(\d{3})/g, ",$1").substring(1);
    }
  }

  if (dec && dec.length > 3) {
    dec = dec.replace(/(\d{3})/g, "$1 ");
  }

  return num + (dec? '.' + dec : '');
}
RobG
  • 142,382
  • 31
  • 172
  • 209
  • It's kind of awkward to reverse the string twice; would be nice to have a regex that works for the other way. – Sophie Alpert Jul 22 '11 at 03:28
  • @Ben, you could loop through the string, but I think it would be faster to reverse twice. a pure regex method would require lookbehind, which I don't think JavaScript/ECMAScript have added to standard. – vol7ron Jul 22 '11 at 03:49
  • @vol7ron - you can also use substring to get an even multiple of 3 to add commas to, then put the extra bit back on. – RobG Jul 22 '11 at 08:51
  • Not quite correct: formatNum(-1234) => "-1 234" formatNum(-12345) => " 12345" formatNum(-123456) => "- 123 456" – Shrike Apr 07 '16 at 11:54
  • @Shrike—I didn't see no stinkin' negatives!! Easily accommodated by trimming the sign if there is one and putting it back on at the end. ;-) – RobG Apr 07 '16 at 22:45
1

This worked for me:

function commafy(inVal){
   var arrWhole = inVal.split(".");
   var arrTheNumber = arrWhole[0].split("").reverse();
   var newNum = Array();
   for(var i=0; i<arrTheNumber.length; i++){
          newNum[newNum.length] = ((i%3===2) && (i<arrTheNumber.length-1)) ? "," + arrTheNumber[i]: arrTheNumber[i];
   }
   var returnNum = newNum.reverse().join("");
   if(arrWhole[1]){
          returnNum += "." + arrWhole[1];
   }
   return returnNum;
}
CLaFarge
  • 1,277
  • 11
  • 16
1

Here you go edited after reading your comments.

function commafy( arg ) {
   arg += '';                                         // stringify
   var num = arg.split('.');                          // incase decimals
   if (typeof num[0] !== 'undefined'){
      var int = num[0];                               // integer part
      if (int.length > 4){
         int     = int.split('').reverse().join('');  // reverse
         int     = int.replace(/(\d{3})/g, "$1,");    // add commas
         int     = int.split('').reverse().join('');  // unreverse
      }
   }
   if (typeof num[1] !== 'undefined'){
      var dec = num[1];                               // float part
      if (dec.length > 4){
         dec     = dec.replace(/(\d{3})/g, "$1 ");    // add spaces
      }
   }

   return (typeof num[0] !== 'undefined'?int:'') 
        + (typeof num[1] !== 'undefined'?'.'+dec:'');
}
vol7ron
  • 40,809
  • 21
  • 119
  • 172
0

Assuming your usage examples are not representative of already-working code but instead desired behavior, and you are looking for help with the algorithm, I think you are already on the right track with splitting on any decimals.

Once split, apply the existing regex to the left side, a similiar regex adding the spaces instead of commas to the right, and then rejoin the the two into a single string before returning.

Unless, of course, there are other considerations or I have misunderstood your question.

Dan Witkowski
  • 351
  • 1
  • 4
0

This is basically the same as the solution from Ghostoy, but it fixes an issue where numbers in the thousands are not handled properly. Changed '5' to '4':

export function commafy(num) {
    const str = num.toString().split('.');
    if (str[0].length >= 4) {
        str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1,');
    }
    if (str[1] && str[1].length >= 4) {
        str[1] = str[1].replace(/(\d{3})/g, '$1 ');
    }
    return str.join('.');
}
0
//Code in Java
private static String formatNumber(String myNum) {
    char[] str = myNum.toCharArray();
    int numCommas = str.length / 3;
    char[] formattedStr = new char[str.length + numCommas];

    for(int i = str.length - 1, j = formattedStr.length - 1, cnt = 0; i >= 0 && j >=0 ;) {
        if(cnt != 0 && cnt % 3 == 0 && j > 0) {
            formattedStr[j] = ',';
            j--;
        }

        formattedStr[j] = str[i];
        i--;
        j--;
        cnt++;
    }
    return String.valueOf(formattedStr);
}
Manish Sinha
  • 221
  • 1
  • 2
  • 6
0

You can do it mathematically, depending on how many digits you want to separate, you can start from one digit with 10 to 100 for 2, and so on.

function splitDigits(num) {
    num=Math.ceil(num);
    let newNum = '';
    while (num > 1000){
      let remain = num % 1000;
      num = Math.floor(num / 1000);
      newNum = remain + ',' + newNum;
    }
    return num + ',' + newNum.slice(0,newNum.length-1);
  }
Ghazaleh H
  • 11
  • 2
0

At first you should select the input with querySelector like:

let field = document.querySelector("input");

and then

        field.addEventListener("keyup", () => {
            for (let i = 1 ; i <= field.value.length; i++) {
                field.value = field.value.replace(",", "");
            }
            let counter=0;

            for (let i = 1 ; i <= field.value.length; i++) {
                if ( i  % ((3 * (counter+1) ) + counter) ===0){
                    let tempVal =field.value
                    field.value = addStr(tempVal,field.value.length - i,",")
                    counter++;
                    console.log(field.value);
                }
            }
            // field.value = parseInt(field.value.replace(/\D/g, ''), 10);
            // var n = parseInt(e.target.value.replace(/\D/g,''),10);
            // e.target.value = n.toLocaleString();
        });
BDL
  • 21,052
  • 22
  • 49
  • 55
mirarab
  • 1
  • 2
0

I found this little function everywhere when searching for "adding commas to a number", and it works great for most cases. It didn't work for me because I was adding commas a dollar amount in a form, and wanted to do so after every keystroke. In my case it worked up to 9999, but once you get to five digits it would result in something like this - 1,4,000. By adding one line of code I was able to strip all commas prior to the function call after each keystroke like so:

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

    $("#dollarAmount").on("keyup change",function() {
      var num = $this).val();
      num = num.replaceAll(',', '');
      var commaNum = addSomeCommas(num);
      $(this).val(commaNum);
    });