Replace with the regular expression - javascript

What regular expression I need to use to correct
if (text.indexOf("+") != -1) {
action = "+";
} else if (text.indexOf("-") != -1) {
action = "-";
} else if (text.indexOf("*") != -1) {
action = "*";
} else if (text.indexOf("/") != -1) {
action = "/";
}
this code
?
EDIT:
and how can I improve this code:
switch (action) {
case "+":
result = parseInt(array[0]) + parseInt(array[1]);
break;
case "-":
result = parseInt(array[0]) - parseInt(array[1]);
break;
case "*":
result = parseInt(array[0]) * parseInt(array[1]);
break;
case "/":
result = parseInt(array[0]) / parseInt(array[1]);
break;
default:
break;
}
Sorry for dull questions I am new in js.

You can use either of these:
var action = text.replace(/.*([+*/-]).*/,'$1');
var match = /[*+/-]/.exec(text);
var action = match && match[0];
If there's the possibility of newlines in your text then change the first to:
var action = text.replace(/[\d\D]*([+*/-])[\d\D]*/,'$1');
Edit: You can improve your switch statement by using, e.g.
// allow floating point values, and also
// don't accidentally interpret "011" as octal
array[0]*1 + array[1]*1;
For more details on why parseInt is probably bad, see this answer.
You can also remove the superfluous default section of your case statement. However, I suspect that your desired "improvement" was making fewer lines. Given that =-*/ are operators in JavaScript (and not methods), I cannot think of any way to avoid having four calculations (i.e. a switch or if/else if).
Whereas in Ruby you could, for example, use array[0].send(action,array[1]) to cover all four cases ;)

Related

JavaScript adding additional actions after receiving the result

I'm making a calculator in JS that displays commands and the result in a prompt.
I want to add an action that, after receiving the result of the operation, makes it possible to multiply, divide, etc. by another number.
Action plan: First action... = result > choose selector ( +, -, *,/) > choose second number > result --- and so on until you stop pressing enter with a blank field.
I was thinking of starting with a while loop to add the ability to choose a character but I don't know if it will affect the result of the previous action, additionally I don't know how to add the ability to choose the next number
switch (operator) {
case '+':
alert(numb1 + numb2)
break
case '-':
alert(numb1 - numb2)
break
case '*':
alert(numb1 * numb2)
break
case '/':
alert(numb1 / numb2)
break
case '%':
alert(numb1 % numb2)
break
}
while (true) {
let result = +prompt('Enter an arithmetic operator or leave blank.')
if (!result) break
}
}
Yes you can use while loop with a new param to get the new value input with prompt, such that:
while (true) {
let result = +prompt('Enter an arithmetic operator or leave blank.')
if (!result) break
let newNumber = +prompt('Enter a new number')
// do the arithmetic operation
}
Additionally, the operation in switch seems to be quite redundant. i.e.: one case for alert in one character / operator change.
You might want to use eval() for the operation.
let numb1 = 1 // original number
while (true) {
let result = prompt('Enter an arithmetic operator or leave blank.')
if (!result) break
let newNumber = prompt('Enter a new number')
// do the arithmetic operation
// if numb1 = 1, new Number = 2, result = '+', it will show '1 + 2'
alert(`${numb1} ${result} ${newNumber}`)
// numb1 = 3
numb1 = eval(`${numb1} ${result} ${newNumber}`)
}
For converting string to operation, please refer to this answer:
https://stackoverflow.com/a/26551015/9067107
This seens like a learning exercise, so I'll try to explain some thought before showing some code.
Your switch case assumes num1 and num2 are already known, but what you described says that num2 will only come after the operator.
Also, you want that math being done inside the loop, else it will only run once.
What you want is:
type first number;
type operator;
type next number;
show result = first/old number (operator) next/last number input;
type operator;
type next number;
show result = first/old number (operator) next/last number input;
... and so on until the "end command" is input.
We have to respect that order, so we'll need to store results and actions. This way we can keep the loop "alive".
Another thing, to be a "good loop", it has to start and end with the same actions.
So, we'll leave the first number input out of the loop, that way it will be something liek this:
ask "first number"
ask "operator" -> (end?) -> ask "next number" -> store and show "result"
ask "operator" -> (end?) -> ask "next number" -> store and show "result"
... and so on ...
// start prompting the user for the first number.
let oldNum = +prompt("first number: ");
let continue = true; // this is the condition to continue or end our loop.
while(continue) {
// prompt the user for the operator
let op = prompt('operator: ');
if (!(op === "+" || op === "-" || op === "/" || op === "*")) break;
// if anything aside from the expected inputs, the loop breaks.
let num = prompt('next number: ');
switch(op) {
case '+':
oldNum += num; // we add the new value to the old one.
break;
case '-':
oldNum -= num;
break;
case '/':
oldNum /= num;
break;
case '*':
oldNum *= num;
break;
}
// show result to user
alert(oldNum);
}
BTW, there are better ways to write this particular code, tried to make it similar to what you shown.

Using switch statement and regex to match the format of url path?

I am trying to use switch-statements with regex conditions to set 'direction' variable depending on the 'url'. I have these sample 'url':
1) a. /meat/chicken/C/1/edit
b. /meat/chicken/c/2/edit
c. /meat/chicken/new
2) a. /meat/beef/B/1/edit
b. /meat/beef/b/2/edit
c. /meat/beef/new
Type of meat could vary.
My regex conditions/code are true for these urls:
if(/[a-zA-Z]+.[a-zA-Z]+.[a-zA-Z]+.\d+.[edit]+/.test(url)){
switch (true) {
case /chicken/.test(url):
direction = 'chicken/edit'
break;
case /beef/.test(url):
direction = 'beef/edit';
break;
default:
direction = 'edit';
break;
}
} else if(/[a-zA-Z]+.[a-zA-Z]+.[new]+/.test(url)){
switch (true) {
case /chicken/.test(url):
direction = 'chicken/new'
break;
case /beef/.test(url):
direction = 'beef/new';
break;
default:
direction = 'new';
break;
}
}
However, it is true for other urls as well which I do not want, such as:
/meat/beef/
If 'url' is '/meat/beef/', this is currently returning true on the else if-statement, "(else if(/[a-zA-Z]+.[a-zA-Z]+.[new]+/.test(url)){", which I do not want it to be true because it doesn't have the word 'new'.
I just want my if-statements and switch-statements to match and be true for formats of 1) and 2) above.
I am not very fluent in regular expressions and still learning switch-statements. Can anyone help me?
I think this may be simpler.
const urlParts = url.split('/');
const [base, type] = urlParts;
const action = urlParts[urlParts.length - 1]; // get the last
if (base === 'meat' && (action === 'edit' || action === 'new')) {
direction = [type, action].join('/');
}
Routing libs are hard to get right. I'd use some of the ones that already exist or at least read them for ideas. something like find-my-way would be a great read.

Switch combining cases string regex and number

Is there a way to create multiple cases in a single Javascript switch statement?
In my code I receive the value of a field via jQuery.
Is it possible that one case checks for string regex and another for number of the same variable?
I am thinking along the lines of:
var field = $(this).val();
var msg;
switch (field)
{
case field.test('Yes'):
msg = "FOO\n";
break;
case 10:
msg = "BAR\n";
break;
}
Although I saw here: Switch statement for string matching in JavaScript
That the way to use switch on strings is by sending the switch statement a "true" value.
What would be the most concise (and correct!) way to achieve this?
OK, compiling both answers above my code that worked and was most elegant IMO is:
var fieldVal = $(this).val();
var msg;
switch (true)
{
case /Yes/.test(fieldVal):
msg = "FOO";
break;
case fieldVal > 10 :
msg = "BAR";
break;
}
this works as separate if statements since we are evaluating whether or not the case returns true but in a clearer and more concise way that could give us the option to add totally disparate test statements in one switch.
the reason it works is probably that the case expression evaluated is interpreted as a true or false value and then checked against the main -
switch(true)
You can't the case need to single value, that's compared to switch expression, but you can put multiple cases to execute the same code:
switch (field) {
case 'Yes':
case 'yes':
case 'YES':
msg = "FOO\n";
break;
case 10:
msg = "BAR\n";
break;
}
but in order to use test as case you can pass true to switch (I found that trick in some open source project):
switch (true) {
case field.test('Yes'):
msg = "FOO\n";
break;
case field == 10:
msg = "BAR\n";
break;
}
but this is the same as if/else
Note: you're using test() incorrectly, it's a method of a regex object, so you need /Yes/.test(field) rather than field.test('Yes'). Anyway...
If you've only got two cases as shown then I'd use an if/else/else if structure:
var field = $(this).val();
var msg;
if(/Yes/.test(field)) {
msg = "FOO\n";
} else if (field === 10) {
msg = "BAR\n";
}
If you need to add additional cases I'd just add extra if else {} branches on the end.
If you have several specific numeric cases you might consider putting them in a switch with the regex tests in a default at the end:
switch (field) {
case 10:
msg = "BAR\n";
break;
case 30:
msg = "whatever\n";
break;
case 50:
msg = "hi\n";
break;
default:
if (/Yes/.test(field)) {
msg = "FOO\n";
else if (/No|N|False|/i.test(field)) {
msg = "blah\n";
}
break;
}
The switch (true) option you alluded to in the question is really just a messier version of an if/else/else if, so it doesn't really make sense unless you have some fall-through cases:
switch(true)
case /Yes/.test(field):
case /Y/.text(field):
case /Whatever/.text(field):
msg = "FOO\n";
break;
case field == 10:
msg = "BAR\n";
break;
}
...and even then an if with multiple conditions joined by || arguably just as tidy with appropriate newlines, and combining multiple regexes into a single one is probably a lot neater.

trouble with SWITCH javascript always executing default case

well i have this trouble and ive been dealing with but i just cant get it to work
i have this function
function getDirections(dir)
{
var img;
switch(dir)
{
case 0:
img+='N.png';
break;
case 1:
img+='NE.png';
break;
case 2:
img+='E.png';
break;
case 3:
img+='SE.png';
break;
case 4:
img+='S.png';
break;
case 5:
img+='SO.png';
break;
case 6:
img+='O.png';
break;
case 7:
img+='NO.png';
break;
default:
alert('enetered default but direction='+dir);
}
return img;
}
quite simple right? now i have this interval set to 5000 ms to call getDirections(variable), the function works well the first time its called but after that , it always enter in the default clause and it also alerts the 'entered default but direction=dirvalue' , i mean even if dir is a value between 0-7 it will always enter to default: but it would alert the value so it was supossed to enter to one of the cases.
i made the same using else if and it worked so i dont know what its wrong with SWITCH
if(dir==0){img+='N.png';}
else if(dir==1){img+='NE.png';}
else if(dir==2){img+='E.png';}
else if(dir==3){img+='SE.png';}
else if(dir==4){img+='S.png';}
else if(dir==5){img+='SO.png';}
else if(dir==6){img+='O.png';}
else if(dir==7){img+='NO.png';}
That is weird... try to make sure that dir is an int, do this before the switch:
dir = parseInt(dir);
If the alert shows the value correctly it should enter the switch, but still it can "look" correct but be of a different data type. Do the conversion manually to ensure it's an int
I know I'm a bit late to the party, but I thought it might be important for anyone who doesn't understand why the "ifs" worked and the switch didn't. It's likely no one will read this answer, but I found it while searching for something else, so perhaps someone will find this helpful anyway:
Your switch is this:
function getDirections(dir) {
var img;
switch(dir) {
case 0:
img+='N.png';
break;
case 1:
img+='NE.png';
break;
case 2:
img+='E.png';
break;
case 3:
img+='SE.png';
break;
case 4:
img+='S.png';
break;
case 5:
img+='SO.png';
break;
case 6:
img+='O.png';
break;
case 7:
img+='NO.png';
break;
default:
alert('enetered default but direction='+dir);
}
return img;
}
This is not the same as a series of double equals (==) but a series of triple equals (===). It would be equivalent to:
if (dir === 0) {
img+='N.png';
} else if (dir === 1) {
img+='NE.png';
} else if (dir === 2) {
img+='E.png';
} else if (dir === 3) {
img+='SE.png';
} else if (dir === 4) {
img+='S.png';
} else if (dir === 5) {
img+='SO.png';
} else if (dir === 6) {
img+='O.png';
} else if (dir === 7) {
img+='NO.png';
} else {
alert('enetered default but direction='+dir);
}
In the world of "==", the integer 2 IS the same as the string "2", but not in the land of "===".
I'd guess that for some reason dir is being passed in as a string. Try changing case 1: to case '1':
Using an array instead of a chain of if/else blocks or a giant switch statement will be faster, more flexible and less error-prone. Also, you wouldn't have to worry if dir is a number or a string. Instead of:
if(dir==0){img+='N.png';}
else if(dir==1){img+='NE.png';}
else if(dir==2){img+='E.png';}
else if(dir==3){img+='SE.png';}
else if(dir==4){img+='S.png';}
else if(dir==5){img+='SO.png';}
else if(dir==6){img+='O.png';}
else if(dir==7){img+='NO.png';}
you can store the file names in an array:
var images = [
'N.png', 'NE.png', 'E.png', 'SE.png', 'S.png', 'SO.png', 'O.png', 'NO.png'
];
or arguably more readable:
var images = "N.png NE.png E.png SE.png S.png SO.png O.png NO.png".split(' ');
and then use just:
img = images[dir];
Full implementation of getDirections using an array would be:
var images = "N.png NE.png E.png SE.png S.png SO.png O.png NO.png".split(' ');
function getDirections(dir) {
var img = images[dir];
if (!img) {
alert("something");
}
return img;
}
Does it work for you?
If images is used only in that one function then you may want to store it as a property of the function to avoid your namespace pollution like this:
function getDirections(dir) {
var img = getDirections.images[dir];
if (!img) {
alert("something");
}
return img;
}
getDirections.images =
"N.png NE.png E.png SE.png S.png SO.png O.png NO.png".split(' ');
or use a closure.
Hard to explain why, but the default: case also need a break; statement after it like all the other cases.
I just ran the code in FireFox/FireBug and called the function this way
getDirections(0);
getDirections('1');
getDirections("2");
The first one does it correctly and the next two enter default.
They are strings and not integer which is what the cases are looking for.
I added
case "2":
img+='NO2.png';
break;
and then only the middle one entered the default. Obviously there is an issue with the way you are calling the function. It is likely passing a string.
I also used your if-else block (added an else{alert(dir);} and that returned the correct value for each call.
Javascript can do on the fly conversion (I think there's a better word for that) between strings and ints (and others). This is occuring when you do the comparrison using ==. If you change the comparison in the ifs to === you get the same behavior as with the switch block.
I pasted your code into an HTML file and ran it with the following buttons:
<button onclick="alert(getDirections(2))">Switch / Int</button>
<button onclick="alert(getDirections('2'))">Switch / String</button>
<button onclick="alert(getDirections2(2))">If-Else / Int</button>
<button onclick="alert(getDirections2('2'))">If-Else / String</button>
When calling the switch-version with a plain 2, it works as expected. Calling it with '2' makes it drop through to the default line. The if-else version works as expected in both cases. So the issue is probably that switch doesn't do an implicit conversion and == does.

Switch statement for string matching in JavaScript

How do I write a switch for the following conditional?
If the url contains "foo", then settings.base_url is "bar".
The following is achieving the effect required but I've a feeling this would be more manageable in a switch:
var doc_location = document.location.href;
var url_strip = new RegExp("http:\/\/.*\/");
var base_url = url_strip.exec(doc_location)
var base_url_string = base_url[0];
//BASE URL CASES
// LOCAL
if (base_url_string.indexOf('xxx.local') > -1) {
settings = {
"base_url" : "http://xxx.local/"
};
}
// DEV
if (base_url_string.indexOf('xxx.dev.yyy.com') > -1) {
settings = {
"base_url" : "http://xxx.dev.yyy.com/xxx/"
};
}
You can't do it in a switch unless you're doing full string matching; that's doing substring matching. (This isn't quite true, as Sean points out in the comments. See note at the end.)
If you're happy that your regex at the top is stripping away everything that you don't want to compare in your match, you don't need a substring match, and could do:
switch (base_url_string) {
case "xxx.local":
// Blah
break;
case "xxx.dev.yyy.com":
// Blah
break;
}
...but again, that only works if that's the complete string you're matching. It would fail if base_url_string were, say, "yyy.xxx.local" whereas your current code would match that in the "xxx.local" branch.
Update: Okay, so technically you can use a switch for substring matching, but I wouldn't recommend it in most situations. Here's how (live example):
function test(str) {
switch (true) {
case /xyz/.test(str):
display("• Matched 'xyz' test");
break;
case /test/.test(str):
display("• Matched 'test' test");
break;
case /ing/.test(str):
display("• Matched 'ing' test");
break;
default:
display("• Didn't match any test");
break;
}
}
That works because of the way JavaScript switch statements work, in particular two key aspects: First, that the cases are considered in source text order, and second that the selector expressions (the bits after the keyword case) are expressions that are evaluated as that case is evaluated (not constants as in some other languages). So since our test expression is true, the first case expression that results in true will be the one that gets used.
RegExp can be used on the input string with the match method too.
To ensure that we have a match in a case clause, we will test the original str value (that is provided to the switch statement) against the input property of a successful match.
input is a static property of regular expressions that contains the original input string.
When match fails it returns null. To avoid an exception error we use optional chaining operator (or the logical || conditional operator in legacy ES) before accessing the input property.
const str = 'XYZ test';
switch (str) {
case str.match(/^xyz/)?.input:
console.log("Matched a string that starts with 'xyz'");
break;
case str.match(/test/)?.input:
console.log("Matched the 'test' substring");
break;
default:
console.log("Didn't match");
break;
}
Another approach is to use the String() constructor to convert the resulting array that must have only 1 element (no capturing groups) and whole string must be captured with quantifiers (.*) to a string. In case of a failure the null object will become a 'null' string. That may seem less convenient.
const str = 'XYZ test';
switch (str.toLowerCase()) {
case String(str.match(/^xyz.*/i)):
console.log("Matched a string without case sensitivity");
break;
case String(str.match(/.*tes.*/)):
console.log("Matched a string using a substring 'tes'");
break;
}
Anyway, a more elegant solution is to use the test method instead of match, i.e. /^find-this-in/.test(str) with switch (true) which simply returns a boolean value and it's easier to match without case sensitivity.
const str = 'haystack';
switch (true) {
case /^hay.*/i.test(str):
console.log("Matched a string that starts with 'hay'");
break;
}
However using if else else if statements in such scenarios would be readable too
Just use the location.host property
switch (location.host) {
case "xxx.local":
settings = ...
break;
case "xxx.dev.yyy.com":
settings = ...
break;
}
Another option is to use input field of a regexp match result:
str = 'XYZ test';
switch (str) {
case (str.match(/^xyz/) || {}).input:
console.log("Matched a string that starts with 'xyz'");
break;
case (str.match(/test/) || {}).input:
console.log("Matched the 'test' substring");
break;
default:
console.log("Didn't match");
break;
}
var token = 'spo';
switch(token){
case ( (token.match(/spo/) )? token : undefined ) :
console.log('MATCHED')
break;;
default:
console.log('NO MATCH')
break;;
}
--> If the match is made the ternary expression returns the original token
----> The original token is evaluated by case
--> If the match is not made the ternary returns undefined
----> Case evaluates the token against undefined which hopefully your token is not.
The ternary test can be anything for instance in your case
( !!~ base_url_string.indexOf('xxx.dev.yyy.com') )? xxx.dev.yyy.com : undefined
===========================================
(token.match(/spo/) )? token : undefined )
is a ternary expression.
The test in this case is token.match(/spo/) which states the match the string held in token against the regex expression /spo/ ( which is the literal string spo in this case ).
If the expression and the string match it results in true and returns token ( which is the string the switch statement is operating on ).
Obviously token === token so the switch statement is matched and the case evaluated
It is easier to understand if you look at it in layers and understand that the turnery test is evaluated "BEFORE" the switch statement so that the switch statement only sees the results of the test.
It may be easier. Try to think like this:
first catch a string between regular characters
after that find "case"
:
// 'www.dev.yyy.com'
// 'xxx.foo.pl'
var url = "xxx.foo.pl";
switch (url.match(/\..*.\./)[0]){
case ".dev.yyy." :
console.log("xxx.dev.yyy.com");break;
case ".some.":
console.log("xxx.foo.pl");break;
} //end switch
Might be too late and all, but I liked this in case assignment :)
function extractParameters(args) {
function getCase(arg, key) {
return arg.match(new RegExp(`${key}=(.*)`)) || {};
}
args.forEach((arg) => {
console.log("arg: " + arg);
let match;
switch (arg) {
case (match = getCase(arg, "--user")).input:
case (match = getCase(arg, "-u")).input:
userName = match[1];
break;
case (match = getCase(arg, "--password")).input:
case (match = getCase(arg, "-p")).input:
password = match[1];
break;
case (match = getCase(arg, "--branch")).input:
case (match = getCase(arg, "-b")).input:
branch = match[1];
break;
}
});
};
you could event take it further, and pass a list of option and handle the regex with |
Self-contained version that increases job security:
switch((s.match(r)||[null])[0])
function identifyCountry(hostname,only_gov=false){
const exceptionRe = /^(?:uk|ac|eu)$/ ; //https://en.wikipedia.org/wiki/Country_code_top-level_domain#ASCII_ccTLDs_not_in_ISO_3166-1
const h = hostname.split('.');
const len = h.length;
const tld = h[len-1];
const sld = len >= 2 ? h[len-2] : null;
if( tld.length == 2 ) {
if( only_gov && sld != 'gov' ) return null;
switch( ( tld.match(exceptionRe) || [null] )[0] ) {
case 'uk':
//Britain owns+uses this one
return 'gb';
case 'ac':
//Ascension Island is part of the British Overseas territory
//"Saint Helena, Ascension and Tristan da Cunha"
return 'sh';
case null:
//2-letter TLD *not* in the exception list;
//it's a valid ccTLD corresponding to its country
return tld;
default:
//2-letter TLD *in* the exception list (e.g.: .eu);
//it's not a valid ccTLD and we don't know the country
return null;
}
} else if( tld == 'gov' ) {
//AMERICAAA
return 'us';
} else {
return null;
}
}
<p>Click the following domains:</p>
<ul onclick="console.log(`${identifyCountry(event.target.textContent)} <= ${event.target.textContent}`);">
<li>example.com</li>
<li>example.co.uk</li>
<li>example.eu</li>
<li>example.ca</li>
<li>example.ac</li>
<li>example.gov</li>
</ul>
Honestly, though, you could just do something like
function switchableMatch(s,r){
//returns the FIRST match of r on s; otherwise, null
const m = s.match(r);
if(m) return m[0];
else return null;
}
and then later switch(switchableMatch(s,r)){…}
You could also make use of the default case like this:
switch (name) {
case 't':
return filter.getType();
case 'c':
return (filter.getCategory());
default:
if (name.startsWith('f-')) {
return filter.getFeatures({type: name})
}
}
If you need to use a regular expression, create an object with regular expressions and a conditional response for the switch case
let test = (str) => {
let obj = {
'foo':'bar',
'\/albums?':'photo'
};
for(let prop in obj){
if(new RegExp(prop).test(str))return obj[prop]
};
};
switch(test(location.href)){
case 'bar':
console.log('url has bar')
break;
}

Categories

Resources