How can I parse a string with a comma thousand separator to a number?
Yes remove the commas:
let output = parseFloat("2,299.00".replace(/,/g, ''));
console.log(output);
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:
- https://github.com/google/closure-library/tree/master/closure/goog/i18n
- https://github.com/globalizejs/globalize
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.
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, "");
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.