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);
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.
Caveat: This won't work for numbers in scientific notation (like 1e3 for one thousand).
Remove anything that isn't a digit, decimal separator, or minus sign (-) (or optionally, a + if you want to allow a unary + on the number).
If you can assume that . is the decimal separator (it isn't in many parts of the world; keep reading), that might look like this:
function convertToFloat(str) {
let body = str;
let sign = "";
const signMatch = /^\s*(-|\+)/.exec(str);
// Or if you don't want to support unary +:
// const signMatch = /^\s*(-)/.exec(str);
if (signMatch) {
body = str.substring(signMatch.index + 1);
sign = signMatch[1];
}
const updatedBody = str.replace(/[^\d\.]/g, "");
const num = parseFloat(sign + updatedBody);
return num;
}
Live Example (I've added a fractional portion to the number just to show that working):
function convertToFloat(str) {
let body = str;
let sign = "";
const signMatch = /^\s*(-|\+)/.exec(str);
// Or if you don't want to support unary +:
// const signMatch = /^\s*(-)/.exec(str);
if (signMatch) {
body = str.substring(signMatch.index + 1);
sign = signMatch[1];
}
const updatedBody = str.replace(/[^\d\.]/g, "");
const num = parseFloat(sign + updatedBody);
return num;
}
console.log(convertToFloat("2,299.23"));
If you want to support locales where . isn't the decimal separator (there are many), you can detect the decimal separator and use the detected one in your regular expression. Here's an example function for finding the decimal separator:
function findDecimalSeparator() {
const num = 1.2;
if (typeof Intl === "object" && Intl && Intl.NumberFormat) {
// I'm surprised it's this much of a pain and am hoping I'm missing
// something in the API
const formatter = new Intl.NumberFormat();
const parts = formatter.formatToParts(num);
const decimal = parts.find(({ type }) => type === "decimal").value;
return decimal;
}
// Doesn't support `Intl.NumberFormat`, fall back to dodgy means
const str = num.toLocaleString();
const parts = /1(\D+)2/.exec(str);
return parts[1];
}
Then convertToFloat looks like:
const decimal = findDecimalSeparator();
function convertToFloat(str) {
let body = str;
let sign = "";
const signMatch = /^\s*(-|\+)/.exec(str);
// Or if you don't want to support unary +:
// const signMatch = /^\s*(-)/.exec(str);
if (signMatch) {
body = str.substring(signMatch.index + 1);
sign = signMatch[1];
}
const rex = new RegExp(`${escapeRegex(decimal)}|-|\\+|\\D`, "g");
const updatedBody = body.replace(
rex,
(match) => match === decimal ? "." : ""
);
const num = parseFloat(sign + updatedBody);
return num;
}
Live Example:
const decimal = findDecimalSeparator();
function findDecimalSeparator() {
const num = 1.2;
if (typeof Intl === "object" && Intl && Intl.NumberFormat) {
// I'm surprised it's this much of a pain and am hoping I'm missing
// something in the API
const formatter = new Intl.NumberFormat();
const parts = formatter.formatToParts(num);
const decimal = parts.find(({ type }) => type === "decimal").value;
return decimal;
}
// Doesn't support `Intl.NumberFormat`, fall back to dodgy means
const str = num.toLocaleString();
const parts = /1(\D+)2/.exec(str);
return parts[1];
}
function escapeRegex(string) {
return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
}
function convertToFloat(str) {
let body = str;
let sign = "";
const signMatch = /^\s*(-|\+)/.exec(str);
// Or if you don't want to support unary +:
// const signMatch = /^\s*(-)/.exec(str);
if (signMatch) {
body = str.substring(signMatch.index + 1);
sign = signMatch[1];
}
const rex = new RegExp(`${escapeRegex(decimal)}|-|\\+|\\D`, "g");
const updatedBody = body.replace(
rex,
(match) => match === decimal ? "." : ""
);
const num = parseFloat(sign + updatedBody);
return num;
}
function gid(id) {
const element = document.getElementById(id);
if (!element) {
throw new Error(`No element found for ID ${JSON.stringify(id)}`);
}
return element;
}
function onClick(id, handler) {
gid(id).addEventListener("click", handler);
}
onClick("convert", () => {
const str = gid("num").value;
const num = convertToFloat(str);
console.log(`${JSON.stringify(str)} => ${num}`);
});
<div>Enter a number using your locale's grouping and decimal separators, optionally prefaced with a minus sign (<code>-</code>) or plus sign (<code>+</code>):</div>
<input type="text" id="num" value="-123">
<input type="button" id="convert" value="Convert">
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.
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, "."));
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.
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(...).
or try this shorter approach:
const myNum = +('2,299.00'.replace(",",""));
If you have several commas use Regex:
const myNum = +('2,022,233,988.55'.replace(/,/g,""));
// -> myNum = 2022233988.55
Here was my case in an array (for similar use case):
To get the sum of this array:
const numbers = ["11", "7", "15/25", "18/5", "12", "16/25"]
By using parseFloat I would lose the decimals so to get the exact sum I had to first replace the forward slash with dot, then convert the strings to actual numbers.
So:
const currectNumbers = numbers.map(num => +(num.replace("/",".")))
// or the longer approach:
const currectNumbers = numbers
.map(num => num.replace("/","."))
.map(num => parseFloat(num));
This will give me the desired array to be used in reduce method:
currectNumbers = [ 11, 7, 15.25, 18.5, 12, 16.25]
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(''));
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
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, '.'));
};
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"
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, ""));
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, '.'));
}
Based on many great architects here, I've simplified it a bit.
I prefer to use Intl.NumberFormat(undefined) to make it use the best fit mechanism.
If the user, like me, has a Danish keyboard, but prefer the Mac to be english, this helps:
if (Number.isNaN(normalized)) return Number(value.replace(',', '.'));
If this is used in a form, I found that I should use inputMode="numeric" rather than type="number".
function parseNumber(value, locales = undefined) {
if (typeof value !== 'string') return value;
const example = Intl.NumberFormat(locales).format('1.1');
const normalized = Number(value.replace(example.charAt(1), '.'));
if (Number.isNaN(normalized)) return Number(value.replace(',', '.'));
return normalized;
}
/* test */
const tests = [
{
locale: 'en-US',
candidate: 1.123,
expected: 1.123,
},
{
locale: 'en-US',
candidate: '1.123',
expected: 1.123,
},
{
locale: 'fr-FR',
candidate: '33.123',
expected: 33.123,
},
{
locale: 'fr-FR',
candidate: '33,123',
expected: 33.123,
},
{
locale: 'da-DK',
candidate: '45.123',
expected: 45.123,
},
{
locale: 'da-DK',
candidate: '45,123',
expected: 45.123,
},
{
locale: 'en-US',
candidate: '0.123',
expected: 0.123,
},
{
locale: undefined,
candidate: '0,123',
expected: 0.123,
},
];
tests.forEach(({ locale, candidate, expected }) => {
const parsed = parseNumber(candidate, locale);
console.log(`${candidate} as ${typeof candidate} in ${locale}: ${parsed} === ${expected}? ${parsed === expected}`);
});
use this instead
const price = 1234567.89;
const formattedPrice = price.toLocaleString(); // "1,234,567.89"
to be more specific
const formattedPrice = price.toLocaleString('en-US', {style: 'currency', currency: 'USD'}); // "$1,234,567.89"
Related
I have a text box that will have a currency string in it that I then need to convert that string to a double to perform some operations on it.
"$1,100.00" → 1100.00
This needs to occur all client side. I have no choice but to leave the currency string as a currency string as input but need to cast/convert it to a double to allow some mathematical operations.
Remove all non dot / digits:
var currency = "-$4,400.50";
var number = Number(currency.replace(/[^0-9.-]+/g,""));
accounting.js is the way to go. I used it at a project and had very good experience using it.
accounting.formatMoney(4999.99, "€", 2, ".", ","); // €4.999,99
accounting.unformat("€ 1.000.000,00", ","); // 1000000
You can find it at GitHub
Use a regex to remove the formating (dollar and comma), and use parseFloat to convert the string to a floating point number.`
var currency = "$1,100.00";
currency.replace(/[$,]+/g,"");
var result = parseFloat(currency) + .05;
I know this is an old question but wanted to give an additional option.
The jQuery Globalize gives the ability to parse a culture specific format to a float.
https://github.com/jquery/globalize
Given a string "$13,042.00", and Globalize set to en-US:
Globalize.culture("en-US");
You can parse the float value out like so:
var result = Globalize.parseFloat(Globalize.format("$13,042.00", "c"));
This will give you:
13042.00
And allows you to work with other cultures.
I know this is an old question, but CMS's answer seems to have one tiny little flaw: it only works if currency format uses "." as decimal separator.
For example, if you need to work with russian rubles, the string will look like this:
"1 000,00 rub."
My solution is far less elegant than CMS's, but it should do the trick.
var currency = "1 000,00 rub."; //it works for US-style currency strings as well
var cur_re = /\D*(\d+|\d.*?\d)(?:\D+(\d{2}))?\D*$/;
var parts = cur_re.exec(currency);
var number = parseFloat(parts[1].replace(/\D/,'')+'.'+(parts[2]?parts[2]:'00'));
console.log(number.toFixed(2));
Assumptions:
currency value uses decimal notation
there are no digits in the string that are not a part of the currency value
currency value contains either 0 or 2 digits in its fractional part *
The regexp can even handle something like "1,999 dollars and 99 cents", though it isn't an intended feature and it should not be relied upon.
Hope this will help someone.
This example run ok
var currency = "$1,123,456.00";
var number = Number(currency.replace(/[^0-9\.]+/g,""));
console.log(number);
For anyone looking for a solution in 2021 you can use Currency.js.
After much research this was the most reliable method I found for production, I didn't have any issues so far. In addition it's very active on Github.
currency(123); // 123.00
currency(1.23); // 1.23
currency("1.23") // 1.23
currency("$12.30") // 12.30
var value = currency("123.45");
currency(value); // 123.45
typescript
import currency from "currency.js";
currency("$12.30").value; // 12.30
This is my function. Works with all currencies..
function toFloat(num) {
dotPos = num.indexOf('.');
commaPos = num.indexOf(',');
if (dotPos < 0)
dotPos = 0;
if (commaPos < 0)
commaPos = 0;
if ((dotPos > commaPos) && dotPos)
sep = dotPos;
else {
if ((commaPos > dotPos) && commaPos)
sep = commaPos;
else
sep = false;
}
if (sep == false)
return parseFloat(num.replace(/[^\d]/g, ""));
return parseFloat(
num.substr(0, sep).replace(/[^\d]/g, "") + '.' +
num.substr(sep+1, num.length).replace(/[^0-9]/, "")
);
}
Usage : toFloat("$1,100.00") or toFloat("1,100.00$")
// "10.000.500,61 TL" price_to_number => 10000500.61
// "10000500.62" number_to_price => 10.000.500,62
JS FIDDLE: https://jsfiddle.net/Limitlessisa/oxhgd32c/
var price="10.000.500,61 TL";
document.getElementById("demo1").innerHTML = price_to_number(price);
var numberPrice="10000500.62";
document.getElementById("demo2").innerHTML = number_to_price(numberPrice);
function price_to_number(v){
if(!v){return 0;}
v=v.split('.').join('');
v=v.split(',').join('.');
return Number(v.replace(/[^0-9.]/g, ""));
}
function number_to_price(v){
if(v==0){return '0,00';}
v=parseFloat(v);
v=v.toFixed(2).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
v=v.split('.').join('*').split(',').join('.').split('*').join(',');
return v;
}
You can try this
var str = "$1,112.12";
str = str.replace(",", "");
str = str.replace("$", "");
console.log(parseFloat(str));
let thousands_seps = '.';
let decimal_sep = ',';
let sanitizeValue = "R$ 2.530,55".replace(thousands_seps,'')
.replace(decimal_sep,'.')
.replace(/[^0-9.-]+/, '');
// Converting to float
// Result 2530.55
let stringToFloat = parseFloat(sanitizeValue);
// Formatting for currency: "R$ 2.530,55"
// BRL in this case
let floatTocurrency = Number(stringToFloat).toLocaleString('pt-BR', {style: 'currency', currency: 'BRL'});
// Output
console.log(stringToFloat, floatTocurrency);
I know you've found a solution to your question, I just wanted to recommend that maybe you look at the following more extensive jQuery plugin for International Number Formats:
International Number Formatter
How about simply
Number(currency.replace(/[^0-9-]+/g,""))/100;
Works with all currencies and locales. replaces all non-numeric chars (you can have €50.000,00 or $50,000.00) input must have 2 decimal places
jQuery.preferCulture("en-IN");
var price = jQuery.format(39.00, "c");
output is: Rs. 39.00
use jquery.glob.js,
jQuery.glob.all.js
Here's a simple function -
function getNumberFromCurrency(currency) {
return Number(currency.replace(/[$,]/g,''))
}
console.log(getNumberFromCurrency('$1,000,000.99')) // 1000000.99
For currencies that use the ',' separator mentioned by Quethzel Diaz
Currency is in Brazilian.
var currency_br = "R$ 1.343,45";
currency_br = currency_br.replace('.', "").replace(',', '.');
var number_formated = Number(currency_br.replace(/[^0-9.-]+/g,""));
var parseCurrency = function (e) {
if (typeof (e) === 'number') return e;
if (typeof (e) === 'string') {
var str = e.trim();
var value = Number(e.replace(/[^0-9.-]+/g, ""));
return str.startsWith('(') && str.endsWith(')') ? -value: value;
}
return e;
}
This worked for me and covers most edge cases :)
function toFloat(num) {
const cleanStr = String(num).replace(/[^0-9.,]/g, '');
let dotPos = cleanStr.indexOf('.');
let commaPos = cleanStr.indexOf(',');
if (dotPos < 0) dotPos = 0;
if (commaPos < 0) commaPos = 0;
const dotSplit = cleanStr.split('.');
const commaSplit = cleanStr.split(',');
const isDecimalDot = dotPos
&& (
(commaPos && dotPos > commaPos)
|| (!commaPos && dotSplit[dotSplit.length - 1].length === 2)
);
const isDecimalComma = commaPos
&& (
(dotPos && dotPos < commaPos)
|| (!dotPos && commaSplit[commaSplit.length - 1].length === 2)
);
let integerPart = cleanStr;
let decimalPart = '0';
if (isDecimalComma) {
integerPart = commaSplit[0];
decimalPart = commaSplit[1];
}
if (isDecimalDot) {
integerPart = dotSplit[0];
decimalPart = dotSplit[1];
}
return parseFloat(
`${integerPart.replace(/[^0-9]/g, '')}.${decimalPart.replace(/[^0-9]/g, '')}`,
);
}
toFloat('USD 1,500.00'); // 1500
toFloat('USD 1,500'); // 1500
toFloat('USD 500.00'); // 500
toFloat('USD 500'); // 500
toFloat('EUR 1.500,00'); // 1500
toFloat('EUR 1.500'); // 1500
toFloat('EUR 500,00'); // 500
toFloat('EUR 500'); // 500
Such a headache and so less consideration to other cultures for nothing...
here it is folks:
let floatPrice = parseFloat(price.replace(/(,|\.)([0-9]{3})/g,'$2').replace(/(,|\.)/,'.'));
as simple as that.
$ 150.00
Fr. 150.00
€ 689.00
I have tested for above three currency symbols .You can do it for others also.
var price = Fr. 150.00;
var priceFloat = price.replace(/[^\d\.]/g, '');
Above regular expression will remove everything that is not a digit or a period.So You can get the string without currency symbol but in case of " Fr. 150.00 " if you console for output then you will get price as
console.log('priceFloat : '+priceFloat);
output will be like priceFloat : .150.00
which is wrong so you check the index of "." then split that and get the proper result.
if (priceFloat.indexOf('.') == 0) {
priceFloat = parseFloat(priceFloat.split('.')[1]);
}else{
priceFloat = parseFloat(priceFloat);
}
function NumberConvertToDecimal (number) {
if (number == 0) {
return '0.00';
}
number = parseFloat(number);
number = number.toFixed(2).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1");
number = number.split('.').join('*').split('*').join('.');
return number;
}
This function should work whichever the locale and currency settings :
function getNumPrice(price, decimalpoint) {
var p = price.split(decimalpoint);
for (var i=0;i<p.length;i++) p[i] = p[i].replace(/\D/g,'');
return p.join('.');
}
This assumes you know the decimal point character (in my case the locale is set from PHP, so I get it with <?php echo cms_function_to_get_decimal_point(); ?>).
You should be able to handle this using vanilla JS. The Internationalization API is part of JS core: ECMAScript Internationalization API
https://www.w3.org/International/wiki/JavaScriptInternationalization
This answer worked for me: How to format numbers as currency strings
Say I have a float x that has a whole part a and decimal part b i.e. x = a.b. How can I format x in JavaScript such that it'll have at most n digits, as shown below:
const n = 5
format(123.0) == '123'
format(123.01) == '123.01'
format(123.012) == '123.01'
format(0.0123) == '0.0123'
format(0.01234) == '0.0123'
Based on a recently deleted answer that had the logic but didn't really work, I came up with this solution:
const format = (number, digits = 5) => {
const str = number + ''; // convert to string
let [integer, decimal] = str.split('.'); // split by dot delimiter
const integerDigits = integer.length;
if (integerDigits >= digits) {
return integer;
} else {
let decimalDigits = digits - integerDigits;
decimal = Number('0.' + decimal.substring(0, decimalDigits)).toString();
return integer + '.' + decimal.split('.')[1];
}
}
Sorry if the title sounds confusing. Basically what I am trying to do is to split a decimal number like this 0.1000 into two part - 1. 0.1 and 000 so I can render them differently with different styles.
Check out this screenshot
All the numbers are represented in strings. The tricky part is that we cannot split the number using number.split('0') since we only want to split at the first zero that appears after a non-zero integer.
Not sure how I can do this.
If I did not misunderstand what you are trying to achieve, you can do it with a regex that only matches unlimited zeros that are at the end of the given string like follows:
function markNumber(num) {
return num.replace( /(0{1,})$/g, '<span>$1</span>')
}
const number = 1.2345670089
let renderStyle1 = ''
let renderStyle2 = ''
const string = String(number) + '.'
const parts = string.split('.')
const decimals = parts[1]
const decimalsArray = Array.from(decimals);
// From MDN: The findIndex() method returns the index of the first element in the array that satisfies the provided testing function. Otherwise -1 is returned.
const firstIndexOfZero = decimalsArray.findIndex(x => x === '0');
// From MDN: The slice() method returns a shallow copy of a portion of an array into a new array object selected from start to end (end not included) where start and end represent the index of items in that array. The original array will not be modified.
if(firstIndexOfZero === -1){
renderStyle1 = parts[0] + parts[1]
} else {
renderStyle1 = parts[0] + decimalsArray.slice(0, firstIndexOfZero).join('') // using .join method to convert array to string without commas
renderStyle2 = decimalsArray.slice(firstIndexOfZero, decimalsArray.length).join('') // using .join method to convert array to string without commas
}
console.log(renderStyle1) // "1234567"
console.log(renderStyle2) // "0089"
Messy, and, probably, can be improved, but this should work:
let re = /(\d*\.[1-9]*?)(0.*)/;
["1000", "1.01", "1.10", "1.000", "1.34043"].map((str) =>
str.split(re).filter((entry) => entry !== "")
);
Here's my regex function
const number = ['0.1000', '2.534300', '1.2000', '1.004334000'];
function split_float(num) {
const reg = /^(\d*\.\d*[^0])(0*)$/g;
const [, ...matches] = [...num.matchAll(reg)][0];
return matches;
}
console.log(number.map(split_float));
here is my answer. It uses split and substring to achieve what you want. Tried it in w3school's tryit editor. Handles all of your data in screenshot pretty well:
function myFunction() {
var str = "0.01200";
var partone = str.split(".")[0];
var temp = str.split(".")[1];
for (var i=0; i<temp.length; i++){
if (temp[i] != 0 && temp[i+1] == 0){
break;
}
}
var parttwo = temp.substring(i+1);
partone = partone + "." + temp.substring(0, i+1);
var res = "partOne = " + partone + " and partTwo = " + parttwo;
document.getElementById("demo").innerHTML = res;
}
Here is the screenshot:
How can I convert Persian/Arabic numbers to English numbers with a simple function?
arabicNumbers = ["١", "٢", "٣", "٤", "٥", "٦", "٧", "٨", "٩", "٠"]
persianNumbers = ["۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹", "۰"]
It is the same schema, but the code pages are different.
Oneliner of all 6 possible translations between English, Arabic, and persian Digits.
Caution!! Please note that this solution is not efficient and therefore is not recommended for production code. It is only good as a oneliner. In these methods the '۰۱۲۳۴۵۶۷۸۹' string is created every time for every digit! It is much wiser to create the string once and store it in a variable and use that variable instead. Also, most likely a simple for-loop is much faster!
const e2p = s => s.replace(/\d/g, d => '۰۱۲۳۴۵۶۷۸۹'[d])
const e2a = s => s.replace(/\d/g, d => '٠١٢٣٤٥٦٧٨٩'[d])
const p2e = s => s.replace(/[۰-۹]/g, d => '۰۱۲۳۴۵۶۷۸۹'.indexOf(d))
const a2e = s => s.replace(/[٠-٩]/g, d => '٠١٢٣٤٥٦٧٨٩'.indexOf(d))
const p2a = s => s.replace(/[۰-۹]/g, d => '٠١٢٣٤٥٦٧٨٩'['۰۱۲۳۴۵۶۷۸۹'.indexOf(d)])
const a2p = s => s.replace(/[٠-٩]/g, d => '۰۱۲۳۴۵۶۷۸۹'['٠١٢٣٤٥٦٧٨٩'.indexOf(d)])
e2p("asdf1234") // asdf۱۲۳۴
e2a("asdf1234") // asdf١٢٣٤
p2e("asdf۱۲۳۴") // asdf1234
a2e("asdf١٢٣٤") // asdf1234
p2a("asdf۱۲۳۴") // asdf١٢٣٤
a2p("asdf١٢٣٤") // asdf۱۲۳۴
Explaination:
(s => f(s))(x) is a lambda function that is immediately executed, and will be equal to f(x)
s.replace(pattern, function) looks for matches of pattern in s, for every match m it will replace m with function(m) in the string.
/\d/g is a regex pattern, \d means a digit in the English language, g means global. If you don't specify the g it will only match the first occurrence, otherwise it will match all the occurrences.
In this case for every English digit d in the string, that digit will be replaced by '۰۱۲۳۴۵۶۷۸۹'[d] so, 3 will be replaced by the third index in that list('۰۱۲۳۴۵۶۷۸۹') which is '۳'
/[۰-۹]/g is the equivalent regex for Persian digits this time we can't use the same method, before we took advantage of the fact that javascript is dynamically typed and that d is automatically converted from a string(regex match) to a number(array index) (you can do '1234'['1'] in javascript which is the same as '1234'[1])
but this time we can't do that because '1234'['۱'] is invalid. so we use a trick here and use indexOf which is a function that tells us the index of an element in an array(here a character in a string) so, '۰۱۲۳۴۵۶۷۸۹'.indexOf(۳) will give us 3 because '۳' is the third index in the string '۰۱۲۳۴۵۶۷۸۹'
Use this simple function to convert your string
var
persianNumbers = [/۰/g, /۱/g, /۲/g, /۳/g, /۴/g, /۵/g, /۶/g, /۷/g, /۸/g, /۹/g],
arabicNumbers = [/٠/g, /١/g, /٢/g, /٣/g, /٤/g, /٥/g, /٦/g, /٧/g, /٨/g, /٩/g],
fixNumbers = function (str)
{
if(typeof str === 'string')
{
for(var i=0; i<10; i++)
{
str = str.replace(persianNumbers[i], i).replace(arabicNumbers[i], i);
}
}
return str;
};
Be careful, in this code the persian numbers codepage are different with the arabian numbers.
Example
var mystr = 'Sample text ۱۱۱۵۱ and ٢٨٢٢';
mystr = fixNumbers(mystr);
Refrence
this is a simple way to do that:
function toEnglishDigits(str) {
// convert persian digits [۰۱۲۳۴۵۶۷۸۹]
var e = '۰'.charCodeAt(0);
str = str.replace(/[۰-۹]/g, function(t) {
return t.charCodeAt(0) - e;
});
// convert arabic indic digits [٠١٢٣٤٥٦٧٨٩]
e = '٠'.charCodeAt(0);
str = str.replace(/[٠-٩]/g, function(t) {
return t.charCodeAt(0) - e;
});
return str;
}
an example:
console.log(toEnglishDigits("abc[0123456789][٠١٢٣٤٥٦٧٨٩][۰۱۲۳۴۵۶۷۸۹]"));
// expected result => abc[0123456789][0123456789][0123456789]
best way to do that return index of number in array:
String.prototype.toEnglishDigits = function () {
return this.replace(/[۰-۹]/g, function (chr) {
var persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
return persian.indexOf(chr);
});
};
The most High Performance (Fast & Accurate) function that can support both Persian/Arabic digits (Unicode numeral characters) is this:
function toEnDigit(s) {
return s.replace(/[\u0660-\u0669\u06f0-\u06f9]/g, // Detect all Persian/Arabic Digit in range of their Unicode with a global RegEx character set
function(a) { return a.charCodeAt(0) & 0xf } // Remove the Unicode base(2) range that not match
)
}
sample='English: 0123456789 - Persian: ۰۱۲۳۴۵۶۷۸۹ - Arabic: ٠١٢٣٤٥٦٧٨٩';
// English: 0123456789 - Persian: 0123456789 - Arabic: 0123456789
console.log( toEnDigit(sample) );
How it work
First by using replace() + RegEx Character Set in range of Arabic Digit Unicode U+0660 - U+0669 = ٠ ... ۹ & Persian Digit Unicode U+06F0 - U+06F9 = ۰ ... ۹ it will detect any character of the string that match it.
Then because Basic Latin Digits (ASCII) have same ends in Unicode U+0030 - U+0039=0-9, So if we remove the difference of them in base, the end can be same.
For that we can use Bitwise AND (&) operation between their Char-code by using charCodeAt() to just the same part stay.
Explain:
// x86 (Base 10) --> Binary (Base 2)
'٤'.charCodeAt(0); // 1636 (Base 10)
'۴'.charCodeAt(0); // 1780 (Base 10)
(1636).toString(2); // 0000000000000000000001100110 0100 (Base 2)
(1780).toString(2); // 0000000000000000000001101111 0100 (Base 2)
(4).toString(2); // 0000000000000000000000000000 0100 (Base 2)
// We need a // 0000000000000000000000000000 1111 (Base 2)
// To And it, for keeping just the 1's
// 0xf = 15
(15).toString(2); // 0000000000000000000000000000 1111 (Base 2)
// So
(
1780 // 0000000000000000000001101111 0100 (Base 2)
& // AND (Operation)
15 // 0000000000000000000000000000 1111 (Base 2)
)
==
4 // 0000000000000000000000000000 0100 (Base 2)
// ---> true
// Also (1636 & 15) == 4 <--- true
Minified version (All Browsers):
function toEnDigit(s){return s.replace(/[\u0660-\u0669\u06f0-\u06f9]/g,function(a){return a.charCodeAt(0)&15})}
OneLiner (Modern Browsers)
const toEnDigit=s=>s.replace(/[٠-٩۰-۹]/g,a=>a.charCodeAt(0)&15);
If the string may contain both "Arabic" and "Persian" numbers then a one-line "replace" can do the job as follows.
The Arabic and Persian numbers are converted to English equivalents. Other text remains unchanged.
Num= "۳٣۶٦۵any٥۵٤۶32٠۰"; // Output should be "33665any55453200"
Num = Num.replace(/[٠-٩]/g, d => "٠١٢٣٤٥٦٧٨٩".indexOf(d)).replace(/[۰-۹]/g, d => "۰۱۲۳۴۵۶۷۸۹".indexOf(d));
console.log(Num);
Short and easy!
"۰۱۲۳۴۵۶۷۸۹".replace(/([۰-۹])/g, function(token) { return String.fromCharCode(token.charCodeAt(0) - 1728); });
Or in a more modern manner
"۰۱۲۳۴۵۶۷۸۹".replace(/([۰-۹])/g, token => String.fromCharCode(token.charCodeAt(0) - 1728));
You could do something like this that uses the index of the number within the string to do the conversion:
// Returns -1 if `fromNum` is not a numeric character
function convertNumber(fromNum) {
var persianNums = '۰١۲۳۴۵۶۷۸۹';
return persianNums.indexOf(fromNum);
}
var testNum = '۴';
alert("number is: " + convertNumber(testNum));
Or map using a object like this:
// Returns -1 if `fromNum` is not a numeric character
function convertNumber(fromNum) {
var result;
var arabicMap = {
'٩': 9,
'٨': 8,
'٧': 7,
'٦': 6,
'٥': 5,
'٤': 4,
'٣': 3,
'٢': 2,
'١': 1,
'٠': 0
};
result = arabicMap[fromNum];
if (result === undefined) {
result = -1;
}
return result;
}
var testNum = '٤';
alert("number is: " + convertNumber(testNum));
Transforms any Persian or Arabic (or mixed) number to "English" numbers (Hindu–Arabic numerals)
var transformNumbers = (function(){
var numerals = {
persian : ["۰", "۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹"],
arabic : ["٠", "١", "٢", "٣", "٤", "٥", "٦", "٧", "٨", "٩"]
};
function fromEnglish(str, lang){
var i, len = str.length, result = "";
for( i = 0; i < len; i++ )
result += numerals[lang][str[i]];
return result;
}
return {
toNormal : function(str){
var num, i, len = str.length, result = "";
for( i = 0; i < len; i++ ){
num = numerals["persian"].indexOf(str[i]);
num = num != -1 ? num : numerals["arabic"].indexOf(str[i]);
if( num == -1 ) num = str[i];
result += num;
}
return result;
},
toPersian : function(str, lang){
return fromEnglish(str, "persian");
},
toArabic : function(str){
return fromEnglish(str, "arabic");
}
}
})();
//////// ON INPUT EVENT //////////////
document.querySelectorAll('input')[0].addEventListener('input', onInput_Normal);
document.querySelectorAll('input')[1].addEventListener('input', onInput_Arabic);
function onInput_Arabic(){
var _n = transformNumbers.toArabic(this.value);
console.clear();
console.log( _n )
}
function onInput_Normal(){
var _n = transformNumbers.toNormal(this.value);
console.clear();
console.log( _n )
}
input{ width:90%; margin-bottom:1em; font-size:1.5em; padding:5px; }
<input placeholder="write in Arabic numerals">
<input placeholder="write in normal numerals">
function toEnglishDigits(str) {
const persianNumbers = ["۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹", "۰"]
const arabicNumbers = ["١", "٢", "٣", "٤", "٥", "٦", "٧", "٨", "٩", "٠"]
const englishNumbers = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
return str.split("").map(c => englishNumbers[persianNumbers.indexOf(c)] ||
englishNumbers[arabicNumbers.indexOf(c)] || c).join("")
}
toEnglishDigits("۶٦۵any٥32") // "665any532"
You can use the new Persian-tools library which is an awesome javascript library to deal with Persian words and numbers. Here is a sample for the task you asked for it:
import { digitsArToFa, digitsArToEn, digitsEnToFa, digitsFaToEn } from "persian-tools2";
digitsArToFa("٠١٢٣٤٥٦٧٨٩"); // "۰۱۲۳۴۵۶۷۸۹"
digitsArToEn("٠١٢٣٤٥٦٧٨٩"); // "0123456789"
digitsEnToFa("123۴۵۶"); // "۱۲۳۴۵۶"
digitsFaToEn("۰۱۲۳۴۵۶۷۸۹"); // "0123456789"
You can also find many other useful functionalities on the repository page of the library.
Based on MMMahdy-PAPION method, a short one-line to convert both Persian and Arabic numbers to English numbers and keep all other characters unchanged is the following:
const toEnDigit=n=>n.replace(/[٠-٩۰-۹]/g,n=>15&n.charCodeAt(0));
const toEnDigit=n=>n.replace(/[٠-٩۰-۹]/g,n=>15&n.charCodeAt(0));
sample='English: 0123456789 - Persian (فارسی): ۰۱۲۳۴۵۶۷۸۹ - Arabic (عربي): ٠١٢٣٤٥٦٧٨٩';
// English: 0123456789 - Persian: 0123456789 - Arabic: 0123456789
console.log(toEnDigit(sample) );
For React solution using typescript this might be useful:
// https://gist.github.com/alieslamifard/364862613408a98139da3cab40abbeb9
import React, { InputHTMLAttributes, useEffect, useRef } from 'react';
// Persian/Arabic To English Digit
const f2e = (event) => {
event.target.value = event.target.value
.replace(/[٠-٩]/g, (d) => '٠١٢٣٤٥٦٧٨٩'.indexOf(d))
.replace(/[۰-۹]/g, (d) => '۰۱۲۳۴۵۶۷۸۹'.indexOf(d));
return event;
};
const useForwardedRef = (ref) => {
const innerRef = useRef(null);
useEffect(() => {
if (!ref) return;
if (typeof ref === 'function') {
ref(innerRef.current);
} else {
ref.current = innerRef.current;
}
}, [ref]);
return innerRef;
};
const Input = React.forwardRef<HTMLInputElement, InputHTMLAttributes<HTMLInputElement>>(
(props, ref) => {
const innerRef = useForwardedRef(ref);
useEffect(() => {
innerRef.current?.addEventListener('keyup', f2e);
return () => {
innerRef.current?.removeEventListener('keyup', f2e);
};
}, [innerRef]);
return <input {...props} ref={innerRef} />;
},
);
export default Input;
Simply use Input instead of native input in your form :)
const convertToPersianDigits = (number) => number.toLocaleString('fa-IR')
convertToPersianDigits(100000) //۱۰۰٬۰۰۰
If you have your number string (a string representing a number) at hand, here is a function called paserNumber that converts that into an actual JS Number object:
function parseNumber(numberText: string) {
return Number(
// Convert Persian (and Arabic) digits to Latin digits
normalizeDigits(numberText)
// Convert Persian/Arabic decimal separator to English decimal separator (dot)
.replace(/٫/g, ".")
// Remove other characters such as thousands separators
.replace(/[^\d.]/g, "")
);
}
const persianDigitsRegex = [/۰/g, /۱/g, /۲/g, /۳/g, /۴/g, /۵/g, /۶/g, /۷/g, /۸/g, /۹/g];
const arabicDigitsRegex = [/٠/g, /١/g, /٢/g, /٣/g, /٤/g, /٥/g, /٦/g, /٧/g, /٨/g, /٩/g];
function normalizeDigits(text: string) {
for (let i = 0; i < 10; i++) {
text = text
.replace(persianDigitsRegex[i], i.toString())
.replace(arabicDigitsRegex[i], i.toString());
}
return text;
}
Note that the parse function is quite forgiving and the number string can be a combination of Persian/Arabic/Latin numerals and separators.
After getting a Number you can format it however you want with Number.toLocaleString function:
let numberString = "۱۲۳۴.5678";
let number = parseNumber(numberString);
val formatted1 = number.toLocaleString("fa"); // OR "fa-IR" for IRAN
val formatted2 = number.toLocaleString("en"); // OR "en-US" for USA
val formatted3 = number.toLocaleString("ar-EG"); // OR "ar" which uses western numerals
For more information about formatting numbers, refer to this answer.
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);
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.
Caveat: This won't work for numbers in scientific notation (like 1e3 for one thousand).
Remove anything that isn't a digit, decimal separator, or minus sign (-) (or optionally, a + if you want to allow a unary + on the number).
If you can assume that . is the decimal separator (it isn't in many parts of the world; keep reading), that might look like this:
function convertToFloat(str) {
let body = str;
let sign = "";
const signMatch = /^\s*(-|\+)/.exec(str);
// Or if you don't want to support unary +:
// const signMatch = /^\s*(-)/.exec(str);
if (signMatch) {
body = str.substring(signMatch.index + 1);
sign = signMatch[1];
}
const updatedBody = str.replace(/[^\d\.]/g, "");
const num = parseFloat(sign + updatedBody);
return num;
}
Live Example (I've added a fractional portion to the number just to show that working):
function convertToFloat(str) {
let body = str;
let sign = "";
const signMatch = /^\s*(-|\+)/.exec(str);
// Or if you don't want to support unary +:
// const signMatch = /^\s*(-)/.exec(str);
if (signMatch) {
body = str.substring(signMatch.index + 1);
sign = signMatch[1];
}
const updatedBody = str.replace(/[^\d\.]/g, "");
const num = parseFloat(sign + updatedBody);
return num;
}
console.log(convertToFloat("2,299.23"));
If you want to support locales where . isn't the decimal separator (there are many), you can detect the decimal separator and use the detected one in your regular expression. Here's an example function for finding the decimal separator:
function findDecimalSeparator() {
const num = 1.2;
if (typeof Intl === "object" && Intl && Intl.NumberFormat) {
// I'm surprised it's this much of a pain and am hoping I'm missing
// something in the API
const formatter = new Intl.NumberFormat();
const parts = formatter.formatToParts(num);
const decimal = parts.find(({ type }) => type === "decimal").value;
return decimal;
}
// Doesn't support `Intl.NumberFormat`, fall back to dodgy means
const str = num.toLocaleString();
const parts = /1(\D+)2/.exec(str);
return parts[1];
}
Then convertToFloat looks like:
const decimal = findDecimalSeparator();
function convertToFloat(str) {
let body = str;
let sign = "";
const signMatch = /^\s*(-|\+)/.exec(str);
// Or if you don't want to support unary +:
// const signMatch = /^\s*(-)/.exec(str);
if (signMatch) {
body = str.substring(signMatch.index + 1);
sign = signMatch[1];
}
const rex = new RegExp(`${escapeRegex(decimal)}|-|\\+|\\D`, "g");
const updatedBody = body.replace(
rex,
(match) => match === decimal ? "." : ""
);
const num = parseFloat(sign + updatedBody);
return num;
}
Live Example:
const decimal = findDecimalSeparator();
function findDecimalSeparator() {
const num = 1.2;
if (typeof Intl === "object" && Intl && Intl.NumberFormat) {
// I'm surprised it's this much of a pain and am hoping I'm missing
// something in the API
const formatter = new Intl.NumberFormat();
const parts = formatter.formatToParts(num);
const decimal = parts.find(({ type }) => type === "decimal").value;
return decimal;
}
// Doesn't support `Intl.NumberFormat`, fall back to dodgy means
const str = num.toLocaleString();
const parts = /1(\D+)2/.exec(str);
return parts[1];
}
function escapeRegex(string) {
return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
}
function convertToFloat(str) {
let body = str;
let sign = "";
const signMatch = /^\s*(-|\+)/.exec(str);
// Or if you don't want to support unary +:
// const signMatch = /^\s*(-)/.exec(str);
if (signMatch) {
body = str.substring(signMatch.index + 1);
sign = signMatch[1];
}
const rex = new RegExp(`${escapeRegex(decimal)}|-|\\+|\\D`, "g");
const updatedBody = body.replace(
rex,
(match) => match === decimal ? "." : ""
);
const num = parseFloat(sign + updatedBody);
return num;
}
function gid(id) {
const element = document.getElementById(id);
if (!element) {
throw new Error(`No element found for ID ${JSON.stringify(id)}`);
}
return element;
}
function onClick(id, handler) {
gid(id).addEventListener("click", handler);
}
onClick("convert", () => {
const str = gid("num").value;
const num = convertToFloat(str);
console.log(`${JSON.stringify(str)} => ${num}`);
});
<div>Enter a number using your locale's grouping and decimal separators, optionally prefaced with a minus sign (<code>-</code>) or plus sign (<code>+</code>):</div>
<input type="text" id="num" value="-123">
<input type="button" id="convert" value="Convert">
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.
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, "."));
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.
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(...).
or try this shorter approach:
const myNum = +('2,299.00'.replace(",",""));
If you have several commas use Regex:
const myNum = +('2,022,233,988.55'.replace(/,/g,""));
// -> myNum = 2022233988.55
Here was my case in an array (for similar use case):
To get the sum of this array:
const numbers = ["11", "7", "15/25", "18/5", "12", "16/25"]
By using parseFloat I would lose the decimals so to get the exact sum I had to first replace the forward slash with dot, then convert the strings to actual numbers.
So:
const currectNumbers = numbers.map(num => +(num.replace("/",".")))
// or the longer approach:
const currectNumbers = numbers
.map(num => num.replace("/","."))
.map(num => parseFloat(num));
This will give me the desired array to be used in reduce method:
currectNumbers = [ 11, 7, 15.25, 18.5, 12, 16.25]
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(''));
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
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, '.'));
};
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"
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, ""));
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, '.'));
}
Based on many great architects here, I've simplified it a bit.
I prefer to use Intl.NumberFormat(undefined) to make it use the best fit mechanism.
If the user, like me, has a Danish keyboard, but prefer the Mac to be english, this helps:
if (Number.isNaN(normalized)) return Number(value.replace(',', '.'));
If this is used in a form, I found that I should use inputMode="numeric" rather than type="number".
function parseNumber(value, locales = undefined) {
if (typeof value !== 'string') return value;
const example = Intl.NumberFormat(locales).format('1.1');
const normalized = Number(value.replace(example.charAt(1), '.'));
if (Number.isNaN(normalized)) return Number(value.replace(',', '.'));
return normalized;
}
/* test */
const tests = [
{
locale: 'en-US',
candidate: 1.123,
expected: 1.123,
},
{
locale: 'en-US',
candidate: '1.123',
expected: 1.123,
},
{
locale: 'fr-FR',
candidate: '33.123',
expected: 33.123,
},
{
locale: 'fr-FR',
candidate: '33,123',
expected: 33.123,
},
{
locale: 'da-DK',
candidate: '45.123',
expected: 45.123,
},
{
locale: 'da-DK',
candidate: '45,123',
expected: 45.123,
},
{
locale: 'en-US',
candidate: '0.123',
expected: 0.123,
},
{
locale: undefined,
candidate: '0,123',
expected: 0.123,
},
];
tests.forEach(({ locale, candidate, expected }) => {
const parsed = parseNumber(candidate, locale);
console.log(`${candidate} as ${typeof candidate} in ${locale}: ${parsed} === ${expected}? ${parsed === expected}`);
});
use this instead
const price = 1234567.89;
const formattedPrice = price.toLocaleString(); // "1,234,567.89"
to be more specific
const formattedPrice = price.toLocaleString('en-US', {style: 'currency', currency: 'USD'}); // "$1,234,567.89"