Regex to implement even-odd rule of negative signs - javascript

I want to write a .replace function in JavaScript that implements the even-odd rule of negative signs in algebra. In a series of negative and positive signs:
Case 1: If there's an odd number of negative signs, it's equivalent to a negative sign
Case 2: If there's an even number of negative signs, it's equivalent to a positive sign.
So I would then do .replace(/regex for case1/, "-") and .replace(/regex for case2/, "+"). Any idea on how to do this?
Here are example strings:
\frac{a^{n+-m}}{b} -> \frac{a^{n-m}}{b}
abc+cde=ghj--+--hsnj -> abc+cde=ghj+hsnj

Well you could replace all +s with -- and then substitute accordingly:
expr.replace(/\+/g, '--').replace(/(--)+-/g, '-').replace(/--/g, '+')
Or you could use a function in your .replace to count the number of -s:
expr.replace(/[-+]+/g, function(signs){
return '+-'[signs.replace(/\+/g, '').length % 2];
});

As vks points out, regular expressions can't, strictly speaking, count. You can cancel out pairs as in Andris's answer, but as you can see the regexps get a bit long when you cover all the cases. An alternative is to combine regexp matching with a normal function:
function do_replacement(x) {
return x.replace(/[+-]+/g,
function (r) {
return r.replace(/\+/g, '').length % 2? '-' : '+';
}
);
}
This splits the task into two parts:
Use a regexp to match any sequence of + and -
In the replacement function, remove the +s from the matched string, and count the remaining characters (which, thanks to the original regexp, can only be -s)
Return either a + or a -, based on whether the count is even (i.e., length % 2 is zero) or odd

([^-+]|^)(?:[+]*-[+]*-)*[+]*-[+]*([^+-]) for odd number of hyphens, as seen https://regex101.com/r/fU0vY7/4 , needs to be replaced with $1-$2
([^-+]|^)(?:[+]*-[+]*-[+]*)+([^+-]) for even number of hyphens, as seen https://regex101.com/r/fU0vY7/5 , needs to be replaced with $1+$2
You can use both replaces on the same string. So far everything that I tested worked, including your examples. If something is off, do tell.
It would be more convenient to avoid capture groups, but the lack of lookbehind in javascript forced me to add the capture groups and $1+-$2 respectivelly

function replace(str) {
return str.replace(/[+-]+/g, function(matched, pos, full) {
// + is 0, - is 1
// the counting is same as XOR of all +/- signs
return matched.split('').reduce(function(a,b) {
return a ^ (b == '-');
}, 0)
? '-'
: '+';
});
}

Consider a number say x,
get x%2
if x%2!=0, then '-',
else '+'
This is just an algorithm. I hope you can get your solution from here.

Related

Applying currency format using replace and a regular expression

I am trying to understand some code where a number is converted to a currency format. Thus, if you have 16.9 it converts to $16.90. The problem with the code is if you have an amount over $1,000, it just returns $1, an amount over $2,000 returns $2, etc. Amounts in the hundreds show up fine.
Here is the function:
var _formatCurrency = function(amount) {
return "$" + parseFloat(amount).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
};
(The reason the semicolon is after the bracket is because this function is in itself a statement in another function. That function is not relevant to this discussion.)
I found out that the person who originally put the code in there found it somewhere but didn't fully understand it and didn't test this particular scenario. I myself have not dealt much with regular expressions. I am not only trying to fix it, but to understand how it is working as it is now.
Here's what I've found out. The code between the backslash after the open parenthesis and the backslash before the g is the pattern. The g means global search. The \d means digit, and the (?=\d{3})+\. appears to mean find 3 digits plus a decimal point. I'm not sure I have that right, though, because if that was correct shouldn't it ignore numbers like 5.4? That works fine. Also, I'm not sure what the '$1,' is for. It looks to me like it is supposed to be placed where the digits are, but wouldn't that change all the numbers to $1? Also, why is there a comma after the 1?
Regarding your comment
I was hoping to just edit the regex so it would work properly.
The regex you are currently using is obviously not working for you so I think you should consider alternatives even if they are not too similar, and
Trying to keep the code change as small as possible
Understandable but sometimes it is better to use a code that is a little bit bigger and MORE READABLE than to go with compact and hieroglyphical.
Back to business:
I'm assuming you are getting a string as an argument and this string is composed only of digits and may or may not have a dot before the last 1 or 2 digts. Something like
//input //intended output
1 $1.00
20 $20.00
34.2 $34.20
23.1 $23.10
62516.16 $62,516.16
15.26 $15.26
4654656 $4,654,656.00
0.3 $0.30
I will let you do a pre-check of (assumed) non-valids like 1. | 2.2. | .6 | 4.8.1 | 4.856 | etc.
Proposed solution:
var _formatCurrency = function(amount) {
amount = "$" + amount.replace(/(\d)(?=(\d{3})+(\.(\d){0,2})*$)/g, '$1,');
if(amount.indexOf('.') === -1)
return amount + '.00';
var decimals = amount.split('.')[1];
return decimals.length < 2 ? amount + '0' : amount;
};
Regex break down:
(\d): Matches one digit. Parentheses group things for referencing when needed.
(?=(\d{3})+(\.(\d){0,2})*$). Now this guy. From end to beginning:
$: Matches the end of the string. This is what allows you to match from the end instead of the beginning which is very handy for adding the commas.
(\.(\d){0,2})*: This part processes the dot and decimals. The \. matches the dot. (\d){0,2} matches 0, 1 or 2 digits (the decimals). The * implies that this whole group can be empty.
?=(\d{3})+: \d{3} matches 3 digits exactly. + means at least one occurrence. Finally ?= matches a group after the main expression without including it in the result. In this case it takes three digits at a time (from the end remember?) and leaves them out of the result for when replacing.
g: Match and replace globally, the whole string.
Replacing with $1,: This is how captured groups are referenced for replacing, in this case the wanted group is number 1. Since the pattern will match every digit in the position 3n+1 (starting from the end or the dot) and catch it in the group number 1 ((\d)), then replacing that catch with $1, will effectively add a comma after each capture.
Try it and please feedback.
Also if you haven't already you should (and SO has not provided me with a format to stress this enough) really really look into this site as suggested by Taplar
The pattern is invalid, and your understanding of the function is incorrect. This function formats a number in a standard US currency, and here is how it works:
The parseFloat() function converts a string value to a decimal number.
The toFixed(2) function rounds the decimal number to 2 digits after the decimal point.
The replace() function is used here to add the thousands spearators (i.e. a comma after every 3 digits). The pattern is incorrect, so here is a suggested fix /(\d)(?=(\d{3})+\.)/g and this is how it works:
The (\d) captures a digit.
The (?=(\d{3})+\.) is called a look-ahead and it ensures that the captured digit above has one set of 3 digits (\d{3}) or more + followed by the decimal point \. after it followed by a decimal point.
The g flag/modifier is to apply the pattern globally, that is on the entire amount.
The replacement $1, replaces the pattern with the first captured group $1, which is in our case the digit (\d) (so technically replacing the digit with itself to make sure we don't lose the digit in the replacement) followed by a comma ,. So like I said, this is just to add the thousands separator.
Here are some tests with the suggested fix. Note that it works fine with numbers and strings:
var _formatCurrency = function(amount) {
return "$" + parseFloat(amount).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
};
console.log(_formatCurrency('1'));
console.log(_formatCurrency('100'));
console.log(_formatCurrency('1000'));
console.log(_formatCurrency('1000000.559'));
console.log(_formatCurrency('10000000000.559'));
console.log(_formatCurrency(1));
console.log(_formatCurrency(100));
console.log(_formatCurrency(1000));
console.log(_formatCurrency(1000000.559));
console.log(_formatCurrency(10000000000.559));
Okay, I want to apologize to everyone who answered. I did some further tracing and found out the JSON call which was bringing in the amount did in fact have a comma in it, so it is just parsing that first digit. I was looking in the wrong place in the code when I thought there was no comma in there already. I do appreciate everyone's input and hope you won't think too bad of me for not catching that before this whole exercise. If nothing else, at least I now know how that regex operates so I can make use of it in the future. Now I just have to go about removing that comma.
Have a great day!
Assuming that you are working with USD only, then this should work for you as an alternative to Regular Expressions. I have also included a few tests to verify that it is working properly.
var test1 = '16.9';
var test2 = '2000.5';
var test3 = '300000.23';
var test4 = '3000000.23';
function stringToUSD(inputString) {
const splitValues = inputString.split('.');
const wholeNumber = splitValues[0].split('')
.map(val => parseInt(val))
.reverse()
.map((val, idx, arr) => idx !== 0 && (idx + 1) % 3 === 0 && arr[idx + 1] !== undefined ? `,${val}` : val)
.reverse()
.join('');
return parseFloat(`${wholeNumber}.${splitValues[1]}`).toFixed(2);
}
console.log(stringToUSD(test1));
console.log(stringToUSD(test2));
console.log(stringToUSD(test3));
console.log(stringToUSD(test4));

Regex - getting position with Regex but without using \B [duplicate]

I am using following regex to 'insert' commas into numbers in javascript.
(\d)(?=(\d{3})+(?!\d))
It works very well with integers however when working with decimal numbers it fails cases like 10000.001223456 (result is 1,234,568.0,000,454,554)
What happens regex looks ahead after '.' finds match and replaces it with ,
Example here
I tried remedy it by adding negative lookbehind without luck,
((\d)(?=(\d{3})+(?!\d))(?<!\.))
since '.' can be at any position in sequence and I cannot use * nor +.
How do I make regex that would not match after some specific symbol (in this specific case after '.')?
You can achieve this only in 3 steps:
Split the number into integer and decimal parts
Modify the integer part
Join.
There is no variable-width look-behind in JS that would be very handy here.
var s = ".12345680000454554";
//Beforehand, perhaps, it is a good idea to check if the number has a decimal part
if (s.indexOf(".") > -1) {
var splts = s.split(".");
//alert(splts);
splts[0] = splts[0].replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
//alert(splts[0]);
s = splts.join(".");
alert(s);
}
else
{
alert(s.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'));
}

Javascript numeric input (regex) validation rules

I'm a bit stuck here.
With regards to the input of accounting data, the analyst requested a specific set of rules on the input of decimal data in text boxes.
Since I have not studied regular expressions at this point, and because I have very strict deadlines to attend with each a lot of work, I request your help.
The rules are (on blur):
IE8+ compatible;
Set default at 2 digits behind the comma;
Disallow other characters than ".", "," and numeric characters;
If there are multiple separators in the returned number, only keep the last one.
Explanation:
Employees may have different regional settings, and comma / dot may be used as either decimal or thousands separator.
In case an employee copy - pastes both the thousand and the decimal separator, it has to be ignored.
What I've done so far, but doesn't fulfill the requirements:
http://jsfiddle.net/NxFHL/1/
$('#test_rules.numeric').on('blur', function(){
var myString = $('#test_rules.numeric').val();
myString = parseFloat(myString.replace(/[^\d.-]/g, ''));
myString = toFixed(myString, 2);
console.log(myString);
});
function toFixed(value, precision) {
var power = Math.pow(10, precision || 0);
return
String(Math.round(value * power) / power);
}
The regular expression used doesn't work correctly as it only accepts dot, not comma.
Also I am not sure about how I should make it so that only the last separator stays (so that the thousands separator gets ignored).
Try this function:
function parseInputNum(val) {
var sep = val.lastIndexOf('.') > val.lastIndexOf(',')? '.' : ',';
var arr = val.replace(new RegExp('[^\\d'+sep+']+', 'g'), '')
.match(new RegExp('(\\d+(?:['+sep+']\\d+|))$'));
return arr? arr[1].replace(/[,]/g, '.') : false;
}
You can use this pattern:
^(?:[1-9](?:[0-9]{0,2}(?:([.,])[0-9]{3})?(?:\1[0-9]{3})*|[0-9]*)|0)(?!\1)[.,][0-9]{2}$
This pattern will check numbers like:
123,45
12,45
0.45
123,456,789.12
1234.56
123.456.789,12
but not numbers like:
12.23.45,12
012,345,678,91
1234,567.89
123,456,78
To convert the string into a number you must remove the thousand delimiter before. This can easily be done since the delimiter (if present) is in the capturing group 1. You must probably too replace the , by the . if it is used as decimal separator.

Regex to only allow numbers under 10 digits?

I'm trying to write a regex to verify that an input is a pure, positive whole number (up to 10 digits, but I'm applying that logic elsewhere).
Right now, this is the regex that I'm working with (which I got from here):
^(([1-9]*)|(([1-9]*).([0-9]*)))$
In this function:
if (/^(([1-9]*)|(([1-9]*).([0-9]*)))$/.test($('#targetMe').val())) {
alert('we cool')
} else {
alert('we not')
}
However, I can't seem to get it to work, and I'm not sure if it's the regex or the function. I need to disallow %, . and ' as well. I only want numeric characters. Can anyone point me in the right direction?
You can do this way:
/^[0-9]{1,10}$/
Code:
var tempVal = $('#targetMe').val();
if (/^[0-9]{1,10}$/.test(+tempVal)) // OR if (/^[0-9]{1,10}$/.test(+tempVal) && tempVal.length<=10)
alert('we cool');
else
alert('we not');
Refer LIVE DEMO
var value = $('#targetMe').val(),
re = /^[1-9][0-9]{0,8}$/;
if (re.test(value)) {
// ok
}
Would you need a regular expression?
var value = +$('#targetMe').val();
if (value && value<9999999999) { /*etc.*/ }
var reg = /^[0-9]{1,10}$/;
var checking = reg.test($('#number').val());
if(checking){
return number;
}else{
return false;
}
That's the problem with blindly copying code. The regex you copied is for numbers including floating point numbers with an arbitrary number of digits - and it is buggy, because it wouldn't allow the digit 0 before the decimal point.
You want the following regex:
^[1-9][0-9]{0,9}$
Use this regular expression to match ten digits only:
#"^\d{10}$"
To find a sequence of ten consecutive digits anywhere in a string, use:
#"\d{10}"
Note that this will also find the first 10 digits of an 11 digit number. To search anywhere in the string for exactly 10 consecutive digits.
#"(?<!\d)\d{10}(?!\d)"
check this site here you can learn JS Regular Expiration. How to create this?
https://www.regextester.com/99401

Enhancing regex of thousands separator?

I saw this beautiful script to add thousands separator to js numbers:
function thousandSeparator(n, sep)
{
var sRegExp = new RegExp('(-?[0-9]+)([0-9]{3})'),
sValue = n + '';
if(sep === undefined)
{
sep = ',';
}
while(sRegExp.test(sValue))
{
sValue = sValue.replace(sRegExp, '$1' + sep + '$2');
}
return sValue;
}
usage :
thousandSeparator(5000000.125, '\,') //"5,000,000.125"
However I'm having a trouble accepting the while loop.
I was thinking to change the regex to : '(-?[0-9]+)([0-9]{3})*' asterisk...
but now , how can I apply the replace statement ?
now I will have $1 and $2..$n
how can I enhance the replace func?
p.s. the code is taken from here http://www.grumelo.com/2009/04/06/thousand-separator-in-javascript/
There is no need to use replace, you can just add toLocaleString instead:
console.log((5000000.125).toLocaleString('en'));
More information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString
Your assumption
now i will have $1 and $2..$n
is wrong. You have two groups, because you have two sets of brackets.
(-?[0-9]+)([0-9]{3})*
1. ^^^^^^^^^^
2. ^^^^^^^^^^
And then you repeat the second group. If it matches the second time, it overwrites the result of the first match, when it matches the third time, it overwrites ...
That means when matching is complete, $2 contains the value of the last match of that group.
First approach
(\d)(?=(?:[0-9]{3})+\b)
and replace with
$1,
See it on Regexr
It has the flaw that it does insert the comma also on the right of the dot. (I am working on it.)
Second approach
(\d)(?:(?=\d+(?=[^\d.]))(?=(?:[0-9]{3})+\b)|(?=\d+(?=\.))(?=(?:[0-9]{3})+(?=\.)))
and replace with
$1,
See it on Regexr
So now its getting a bit more complicated.
(\d) # Match a digit (will be reinserted)
(?:
(?=\d+(?=[^\d.])) # Use this alternative if there is no fractional part in the digit
(?=(?:\d{3})+ # Check that there are always multiples of 3 digits ahead
\b) # Till a word boundary
| # OR
(?=\d+(?=\.)) # There is a fractional part
(?=(?:\d{3})+ # Check that there are always multiples of 3 digits ahead
(?=\.)) # Till a dot
)
Problem:
does also match the fractional part if there is not the end of the string following.
Here is an ugly script to contrast your beautiful script.
10000000.0001 .toString().split('').reverse().join('')
.replace(/(\d{3}(?!.*\.|$))/g, '$1,').split('').reverse().join('')
Since we don't have lookbehinds, we can cheat by reversing the string and using lookaheads instead.
Here it is again in a more palatable form.
function thousandSeparator(n, sep) {
function reverse(text) {
return text.split('').reverse().join('');
}
var rx = /(\d{3}(?!.*\.|$))/g;
if (!sep) {
sep = ',';
}
return reverse(reverse(n.toString()).replace(rx, '$1' + sep));
}
How about this one:
result = "1235423.125".replace(/\B(?=(\d{3})+(?!\d))/g, ',') //1,235,423.125
Try this one:
result = subject.replace(/([0-9]+?)([0-9]{3})(?=.*?\.|$)/mg, "$1,$2");
Test here

Categories