Regex - creating an input/textarea that correctly interprets numbers - javascript

Im designing a conversion website where i perform calculations on inputted numbers and i need my input or textarea to receive and interpret numbers entered in different fashions
like:
Entry = 3,000,000.1111
Interpreted value = 3000000.1111
or
Entry = 3000000.1111
Interpreted value = 3000000.1111
and I want to include a second input for European decimal notation
(or if possible have the same input do both)
Entry = 3.000.000,1111 (comma acts a decimal, decimal as separator)
Interpreted value = 3000000.1111
I wonder how I could do this. I suspect from some of my research that I could use regex.
Also should i use an input or a textarea? I want to limit the size of the number to 40 places.
It seems the textarea Im currently using won't recognize any values after a comma when a comma is used. I realized this is due to parseFloat. So I need to remove the commas using .replace() before parsing. But what do I do in the instance of European notation where the comma IS the decimal point? I suspect I should use regex to identify if a number is in comma decimal notation or standard decimal point notation and then outline the appropriate replacement behavior based on that. Any ideas how to write regex to identify a number between .0000000001 and 1,000,000,000,000,000 by only the separator and decimal point? What about when the entry doesn't use either? 12000 for example. Any help with this would be appreciated. Using HTML5 and Javascript. I am not using a form and am new at this. This is my first web page so please be patient with my questions.
I was thinking about this:
input = //value from textarea as a string
if(/REGEX which determines that the structure of the number is N,NNN.NN/.test(input)){
input = input.replace(/\,/,""); //replace the commas with nothing
}
else if(/REGEX which determine that structure of the number is N.NNN,NN/.test(input){
input = input.replace(/\./,""); //replace the decimal point separators with nothing
input = input.replace(/\,/,".");//replace the comma decimal with a point decimal
}
else{
//input unchanged assuming is NNNN without decimal
}
number = parseFloat(input);
I want to keep the possibility open for them to enter large numbers and also to use numbers less than one to 10 decimal places. Thanks to those who contributed.
Best,RP

I believe this should handle everything:
^[1-9](?:\d{0,2}(?:([,.])\d{3})*|\d+)(?:(?!\1)[,.]\d+)?$
You're treading on complicated territory here. Also, the above RegEx does not allow for values less than "1".
Basically, the RegEx does the following:
Allows for no thousandths separators ("," or ".") but ensures if they are used that they occur in the correct places.
Allows for either "," or "." to be used as both thousandths/cents separators, but ensures that the cents separator is not the same as the thousandths separator.
Requires the string equivalent number to begin with any digit other than "0".
To implement this you could attach an event listener to your form element(s) and use JS to do a simple .test.
After reading further, I think I misinterpreted your goal originally. I assumed you simply wanted to validate these values with a RegEx. I also assumed you're trying to work with currency (ie. two decimal places). However, fret not! You can still utilize my original answer if you really want.
You mentioned input and textarea which are both form elements. You can attach a listener to these element(s) looking for the input, change, and/or keyup events. As a part of the callback you can run the .test method or some other functionality. Personally, I would rethink how you want to handle input. Also, what's your actual goal here? Do you really need to know the thousandths separator or keep track of it? Why not just disallow any characters other than the one decimal point/comma and digits?
Also, parsing numbers like .0000000001 as a float is a terrible idea. You will lose precision very quickly if you do any sort of calculations such as multiplication, division, power, etc. You're going to have to figure out a different method to do this like storing the number to the right separately and as integers instead then go from there.
I can help you if you describe what you're trying to do in better detail.

Related

Why doesn't my function correctly replace when using some regex pattern

This is an extension of this SO question
I made a function to see if i can correctly format any number. The answers below work on tools like https://regex101.com and https://regexr.com/, but not within my function(tried in node and browser):
const
const format = (num, regex) => String(num).replace(regex, '$1')
Basically given any whole number, it should not exceed 15 significant digits. Given any decimal, it should not exceed 2 decimal points.
so...
Now
format(0.12345678901234567890, /^\d{1,13}(\.\d{1,2}|\d{0,2})$/)
returns 0.123456789012345678 instead of 0.123456789012345
but
format(0.123456789012345,/^-?(\d*\.?\d{0,2}).*/)
returns number formatted to 2 deimal points as expected.
Let me try to explain what's going on.
For the given input 0.12345678901234567890 and the regex /^\d{1,13}(\.\d{1,2}|\d{0,2})$/, let's go step by step and see what's happening.
^\d{1,13} Does indeed match the start of the string 0
(\. Now you've opened a new group, and it does match .
\d{1,2} It does find the digits 1 and 2
|\d{0,2} So this part is skipped
) So this is the end of your capture group.
$ This indicates the end of the string, but it won't match, because you've still got 345678901234567890 remaining.
Javascript returns the whole string because the match failed in the end.
Let's try removing $ at the end, to become /^\d{1,13}(\.\d{1,2}|\d{0,2})/
You'd get back ".12345678901234567890". This generates a couple of questions.
Why did the preceding 0 get removed?
Because it was not part of your matching group, enclosed with ().
Why did we not get only two decimal places, i.e. .12?
Remember that you're doing a replace. Which means that by default, the original string will be kept in place, only the parts that match will get replaced. Since 345678901234567890 was not part of the match, it was left intact. The only part that matched was 0.12.
Answer to title question: your function doesn't replace, because there's nothing to replace - the regex doesn't match anything in the string. csb's answer explains that in all details.
But that's perhaps not the answer you really need.
Now, it seems like you have an XY problem. You ask why your call to .replace() doesn't work, but .replace() is definitely not a function you should use. Role of .replace() is replacing parts of string, while you actually want to create a different string. Moreover, in the comments you suggest that your formatting is not only for presenting data to user, but you also intend to use it in some further computation. You also mention cryptocurriencies.
Let's cope with these problems one-by-one.
What to do instead of replace?
Well, just produce the string you need instead of replacing something in the string you don't like. There are some edge cases. Instead of writing all-in-one regex, just handle them one-by-one.
The following code is definitely not best possible, but it's main aim is to be simple and show exactly what is going on.
function format(n) {
const max_significant_digits = 15;
const max_precision = 2;
let digits_before_decimal_point;
if (n < 0) {
// Don't count minus sign.
digits_before_decimal_point = n.toFixed(0).length - 1;
} else {
digits_before_decimal_point = n.toFixed(0).length;
}
if (digits_before_decimal_point > max_significant_digits) {
throw new Error('No good representation for this number');
}
const available_significant_digits_for_precision =
Math.max(0, max_significant_digits - digits_before_decimal_point);
const effective_max_precision =
Math.min(max_precision, available_significant_digits_for_precision);
const with_trailing_zeroes = n.toFixed(effective_max_precision);
// I want to keep the string and change just matching part,
// so here .replace() is a proper method to use.
const withouth_trailing_zeroes = with_trailing_zeroes.replace(/\.?0*$/, '');
return withouth_trailing_zeroes;
}
So, you got the number formatted the way you want. What now?
What can you use this string for?
Well, you can display it to the user. And that's mostly it. The value was rounded to (1) represent it in a different base and (2) fit in limited precision, so it's pretty much useless for any computation. And, BTW, why would you convert it to String in the first place, if what you want is a number?
Was the value you are trying to print ever useful in the first place?
Well, that's the most serious question here. Because, you know, floating point numbers are tricky. And they are absolutely abysmal for representing money. So, most likely the number you are trying to format is already a wrong number.
What to use instead?
Fixed-point arithmetic is the most obvious answer. Works most of the time. However, it's pretty tricky in JS, where number may slip into floating-point representation almost any time. So, it's better to use decimal arithmetic library. Optionally, switch to a language that has built-in bignums and decimals, like Python.

Regex to make nondecimal number decimal (add .00)

I have an user input where user can edit price of something. To leave data consistance I would like to manipulate with that string on front-end site.
What I want to do is:
1234 to 1234.00
12.3 to 12.30
12,3 to 12.30
1234.45 to 1234.45
So basicly,
Replace comma with dots
this should be done easy with somehing like:
str.replace(',', '.');
Add dots if number if not decimal and also always change number of digits on two(so add 0 if needed)
I try to do something like:
priceByUser = priceByUser.replace(/^\d*\.?\d*$/, "$1\.00");
unfortunately this really doesnt even work as I expected.
Is there a chance someone can help me to solve this issue?
Thanks
You could consider using a regular expression to replace your commas and periods with just decimal points and then parse the values as floats via parseFloat() then finally, use the toFixed(2) function to indicate that you want two decimal places :
// This will clean up your input (consolidating periods and commas), parse the result
// as a floating point number and then format that number to two decimal places
priceByUser = parseFloat(priceByUser.replace(/,/g,'.')).toFixed(2);
If you wanted an extra-level of validation, you could consider stripping out any non-digit or decimal places after this occurs :
// Sanitize
priceByUser = priceByUser.replace(/,/g,'.').replace(/[^\d\.]/g,'');
// Parse and format
priceByUser = Number(priceByUser).toFixed(2);
Example
You can see a working example here and and example of input/output below :

Validating Floating Point Precision in Javascript

I'm trying to use Javascript to verify that user input is a valid monetary (dollars and cents) amount, with no more than two digits to the right of the decimal point.
Shifting left, then performing a modulo 1 fails because of inherent floating-point issues:
(parseFloat("19.99") * 100) % 1 // Returns 0.9999999999997726
I'm aware of the BigDecimal library, but I'm not sure that converting to BigDecimal and then doing the validation would be the right approach, since it could cover up invalid inputs as well as floating-point issues.
As it stands, my workaround is to test the raw (string) input against the following regex:
/^\d*((\.\d{0,2})?)$/
Is this sufficient to guarantee that the input is a valid float in currency format? And if not, is there a math-based way to do the validation?
if you don't need to use RegExp, let JS's math do the heavy-lifting:
var n=myInput.value;
if(!n || +(+n || 0).toFixed(2) != +n) {
alert("real numbers only!");
myInput.select();
}
the validation routine part is made up and poor UX, but the math is solid and bullet-proof.
Your regex will also match empty input, which is not right.
Try this regex instead:
^(?=[^.])\d*(?:\.\d{0,2})?$
Working Demo
(?=[^.]) is a lookahead that will make sure at least one non-dot character is there in input to avoid matching empty string.

Ensure fixed number of decimal points when using Kendo formatting

I have played around with Kendo formatting. Specifically I am using kendo.format and kendo.toString()
I would like to fix the number of decimal points.
kendo.format("{0:#.#%}",22) works well, but it doesn't include a trailing zero for whole numbers. Ex: It doesn't give me 22.0%.
kendo.toString(22,"p1") can be used to ensure the decimal point, but it adds an undesirable space between the number and the percentage sign.(e.g.22 %).
Is there a way to ensure the trailing 0 in the formatted value (with no space before the percentage sign)? Or do I have to add code to remove the space manually?
I can easily remove it using a simple .replace(" ", ""), but I am just curious if there is a built in way to control it.
You can use zeros instad of the sharp symbols. Thus you ensure there will be a digit rendered even if it is not needed.
e.g.
kendo.format("{0:0.0%}",0.22)
Here is live example, Here is the documentation.

Add comma separator to a value variable

I have read some thousand comma separator JavaScript question/answer but found it hard to apply it in practice. For example I have the variable
x = 10023871234981029898198264897123897.231241235
How will I separate it in thousands with commas? I want a function that not only works with that number of digits but more. Regardless of the number of digits the function I need has to separate the number in commas and leaving the digits after the decimal point as it is, Can anyone help? It has to work on number and turn it into string.
First of all, for such huge numbers you should use string format:
var x = "10023871234981029898198264897123897.231241235";
Otherwise, JavaScript will automatically convert it to exponential notation, i.e. 1.002387123498103e+34.
Then, according to the question about money formatting, you can use the following code:
x.replace(/(\d)(?=(\d{3})+\.)/g, "$1,");
It will result in: "10,023,871,234,981,029,898,198,264,897,123,897.231241235".

Categories

Resources