How can I parse a string with a comma thousand separator to a number?

JavascriptNumber Formatting

Javascript Problem Overview


I have 2,299.00 as a string and I am trying to parse it to a number. I tried using parseFloat, which results in 2. I guess the comma is the problem, but how would I solve this issue the right way? Just remove the comma?

var x = parseFloat("2,299.00")
console.log(x);

Javascript Solutions


Solution 1 - Javascript

Yes remove the commas:

let output = parseFloat("2,299.00".replace(/,/g, ''));
console.log(output);

Solution 2 - Javascript

Removing commas is potentially dangerous because, as others have mentioned in the comments, many locales use a comma to mean something different (like a decimal place).

I don't know where you got your string from, but in some places in the world "2,299.00" = 2.299

The Intl object could have been a nice way to tackle this problem, but somehow they managed to ship the spec with only a Intl.NumberFormat.format() API and no parse counterpart :(

The only way to parse a string with cultural numeric characters in it to a machine recognisable number in any i18n sane way is to use a library that leverages CLDR data to cover off all possible ways of formatting number strings http://cldr.unicode.org/

The two best JS options I've come across for this so far:

Solution 3 - Javascript

On modern browsers you can use the built in Intl.NumberFormat to detect the browser's number formatting and normalize the input to match.

function parseNumber(value, locales = navigator.languages) {
  const example = Intl.NumberFormat(locales).format('1.1');
  const cleanPattern = new RegExp(`[^-+0-9${ example.charAt( 1 ) }]`, 'g');
  const cleaned = value.replace(cleanPattern, '');
  const normalized = cleaned.replace(example.charAt(1), '.');

  return parseFloat(normalized);
}

const corpus = {
  '1.123': {
    expected: 1.123,
    locale: 'en-US'
  },
  '1,123': {
    expected: 1123,
    locale: 'en-US'
  },
  '2.123': {
    expected: 2123,
    locale: 'fr-FR'
  },
  '2,123': {
    expected: 2.123,
    locale: 'fr-FR'
  },
}


for (const candidate in corpus) {
  const {
    locale,
    expected
  } = corpus[candidate];
  const parsed = parseNumber(candidate, locale);

  console.log(`${ candidate } in ${ corpus[ candidate ].locale } == ${ expected }? ${ parsed === expected }`);
}

Their's obviously room for some optimization and caching but this works reliably in all languages.

Solution 4 - Javascript

Remove anything that isn't a digit, decimal point, or minus sign (-):

var str = "2,299.00";
str = str.replace(/[^\d\.\-]/g, ""); // You might also include + if you want them to be able to type it
var num = parseFloat(str);

Updated fiddle

Note that it won't work for numbers in scientific notation. If you want it to, change the replace line to add e, E, and + to the list of acceptable characters:

str = str.replace(/[^\d\.\-eE+]/g, "");

Solution 5 - Javascript

Usually you should consider to use input fields which don't allow free text input for numeric values. But there might be cases, when you need to guess the input format. For example 1.234,56 in Germany means 1,234.56 in US. See https://salesforce.stackexchange.com/a/21404 for a list of countries which use comma as decimal.

I use the following function to do a best guess and strip off all non-numeric characters:

function parseNumber(strg) {
	var strg = strg || "";
	var decimal = '.';
	strg = strg.replace(/[^0-9$.,]/g, '');
	if(strg.indexOf(',') > strg.indexOf('.')) decimal = ',';
	if((strg.match(new RegExp("\\" + decimal,"g")) || []).length > 1) decimal="";
	if (decimal != "" && (strg.length - strg.indexOf(decimal) - 1 == 3) && strg.indexOf("0" + decimal)!==0) decimal = "";
	strg = strg.replace(new RegExp("[^0-9$" + decimal + "]","g"), "");
	strg = strg.replace(',', '.');
	return parseFloat(strg);
}	

Try it here: https://plnkr.co/edit/9p5Y6H?p=preview

Examples:

1.234,56 € => 1234.56
1,234.56USD => 1234.56
1,234,567€ => 1234567
1.234.567 => 1234567
1,234.567 => 1234.567
1.234 => 1234 // might be wrong - best guess
1,234 => 1234 // might be wrong - best guess
1.2345 => 1.2345
0,123 => 0.123

The function has one weak point: It is not possible to guess the format if you have 1,123 or 1.123 - because depending on the locale format both might be a comma or a thousands-separator. In this special case the function will treat separator as a thousands-separator and return 1123.

Solution 6 - Javascript

It's baffling that they included a toLocaleString but not a parse method. At least toLocaleString without arguments is well supported in IE6+.

For a i18n solution, I came up with this:

First detect the user's locale decimal separator:

var decimalSeparator = 1.1;
decimalSeparator = decimalSeparator.toLocaleString().substring(1, 2);

Then normalize the number if there's more than one decimal separator in the String:

var pattern = "([" + decimalSeparator + "])(?=.*\\1)";separator
var formatted = valor.replace(new RegExp(pattern, "g"), "");

Finally, remove anything that is not a number or a decimal separator:

formatted = formatted.replace(new RegExp("[^0-9" + decimalSeparator + "]", "g"), '');
return Number(formatted.replace(decimalSeparator, "."));

Solution 7 - Javascript

If you want to avoid the problem that David Meister posted and you are sure about the number of decimal places, you can replace all dots and commas and divide by 100, ex.:

var value = "2,299.00";
var amount = parseFloat(value.replace(/"|\,|\./g, ''))/100;

or if you have 3 decimals

var value = "2,299.001";
var amount = parseFloat(value.replace(/"|\,|\./g, ''))/1000;

It's up to you if you want to use parseInt, parseFloat or Number. Also If you want to keep the number of decimal places you can use the function .toFixed(...).

Solution 8 - Javascript

This converts a number in whatever locale to normal number. Works for decimals points too:

function numberFromLocaleString(stringValue, locale){
	var parts = Number(1111.11).toLocaleString(locale).replace(/\d+/g,'').split('');
	if (stringValue === null)
		return null;
	if (parts.length==1) {
		parts.unshift('');
	}	
	return Number(String(stringValue).replace(new RegExp(parts[0].replace(/\s/g,' '),'g'), '').replace(parts[1],"."));
}
//Use default browser locale
numberFromLocaleString("1,223,333.567") //1223333.567

//Use specific locale
numberFromLocaleString("1 223 333,567", "ru") //1223333.567

Solution 9 - Javascript

All of these answers fail if you have a number in the millions.

3,456,789 would simply return 3456 with the replace method.

The most correct answer for simply removing the commas would have to be.

var number = '3,456,789.12';
number.split(',').join('');
/* number now equips 3456789.12 */
parseFloat(number);

Or simply written.

number = parseFloat(number.split(',').join(''));

Solution 10 - Javascript

const parseLocaleNumber = strNum => {
    const decSep = (1.1).toLocaleString().substring(1, 2);
    const formatted = strNum
        .replace(new RegExp(`([${decSep}])(?=.*\\1)`, 'g'), '')
        .replace(new RegExp(`[^0-9${decSep}]`, 'g'), '');
    return Number(formatted.replace(decSep, '.'));
};

Solution 11 - Javascript

With this function you will be able to format values in multiple formats like 1.234,56 and 1,234.56, and even with errors like 1.234.56 and 1,234,56

/**
 * @param {string} value: value to convert
 * @param {bool} coerce: force float return or NaN
 */
function parseFloatFromString(value, coerce) {
    value = String(value).trim();

    if ('' === value) {
        return value;
    }

    // check if the string can be converted to float as-is
    var parsed = parseFloat(value);
    if (String(parsed) === value) {
        return fixDecimals(parsed, 2);
    }

    // replace arabic numbers by latin
    value = value
    // arabic
    .replace(/[\u0660-\u0669]/g, function(d) {
        return d.charCodeAt(0) - 1632;
    })

    // persian
    .replace(/[\u06F0-\u06F9]/g, function(d) {
        return d.charCodeAt(0) - 1776;
    });

    // remove all non-digit characters
    var split = value.split(/[^\dE-]+/);

    if (1 === split.length) {
        // there's no decimal part
        return fixDecimals(parseFloat(value), 2);
    }

    for (var i = 0; i < split.length; i++) {
        if ('' === split[i]) {
            return coerce ? fixDecimals(parseFloat(0), 2) : NaN;
        }
    }

    // use the last part as decimal
    var decimal = split.pop();

    // reconstruct the number using dot as decimal separator
    return fixDecimals(parseFloat(split.join('') +  '.' + decimal), 2);
}

function fixDecimals(num, precision) {
    return (Math.floor(num * 100) / 100).toFixed(precision);
}
parseFloatFromString('1.234,56')
"1234.56"
parseFloatFromString('1,234.56')
"1234.56"
parseFloatFromString('1.234.56')
"1234.56"
parseFloatFromString('1,234,56')
"1234.56"

Solution 12 - Javascript

Number("2,299.00".split(',').join(''));   // 2299

The split function splits the string into an array using "," as a separator and returns an array.
The join function joins the elements of the array returned from the split function.
The Number() function converts the joined string to a number.

Solution 13 - Javascript

If you want a l10n answer do it this way. Example uses currency, but you don't need that. Intl library will need to be polyfilled if you have to support older browsers.

var value = "2,299.00";
var currencyId = "USD";
var nf = new Intl.NumberFormat(undefined, {style:'currency', currency: currencyId, minimumFractionDigits: 2});

value = nf.format(value.replace(/,/g, ""));

Solution 14 - Javascript

If you have a small set of locales to support you'd probably be better off by just hardcoding a couple of simple rules:

function parseNumber(str, locale) {
  let radix = ',';
  if (locale.match(/(en|th)([-_].+)?/)) {
    radix = '.';
  }
  return Number(str
    .replace(new RegExp('[^\\d\\' + radix + ']', 'g'), '')
    .replace(radix, '.'));
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1540714View Question on Stackoverflow
Solution 1 - JavascriptSamView Answer on Stackoverflow
Solution 2 - JavascriptDavid MeisterView Answer on Stackoverflow
Solution 3 - JavascriptPaul AlexanderView Answer on Stackoverflow
Solution 4 - JavascriptT.J. CrowderView Answer on Stackoverflow
Solution 5 - JavascriptGerfriedView Answer on Stackoverflow
Solution 6 - JavascriptFábioView Answer on Stackoverflow
Solution 7 - JavascriptFernando LimeiraView Answer on Stackoverflow
Solution 8 - JavascriptEldar GerfanovView Answer on Stackoverflow
Solution 9 - JavascriptCaseView Answer on Stackoverflow
Solution 10 - JavascriptDenys RusovView Answer on Stackoverflow
Solution 11 - JavascriptjoseantgvView Answer on Stackoverflow
Solution 12 - JavascriptBrunoEloView Answer on Stackoverflow
Solution 13 - JavascriptTony TopperView Answer on Stackoverflow
Solution 14 - JavascriptAndreas BaumgartView Answer on Stackoverflow