Is there a better way to write this switch in JavaScript? - javascript

I have a switch with the following conditions:
The first case is or between three names (ROKA, MOKA and TOKA).
The second case is another name (KOKA) but with two additional conditions to display an alert.
Lastly, I have some other conditions to check inside the default block, as I'm unable to use a case for them.
This is my code:
var myname= 'JOKA';
var dhaba = false;
switch (myname) {
case ('ROKA'):
case ('MOKA'):
case ('TOKA'):
alert('EEE');
break;
case ('KOKA'):
// This will work as Goto to final default:
if (condition1 && condition2) {
alert('FEEE');
break;
}
default:
if (dhaba && myname != 'ROKA' && myname != 'TOKA') {
alert('TEEEE');
} else {
alert('CHEEE');
}
}
Is there a better way to write this code?

When you reach default, then myname is always unequal to the previously checked values. It is sufficient to use
default:
if (dhaba) {
alert('TEEEE');
} else {
alert('CHEEE');
}

I think switch is not the best option for your use case.
Also, as #NinaScholz has pointed out, the myname != 'ROKA' && myname != 'TOKA' will always be true, as otherwise you will fall in the first case.
Let's go step by step and see different ways to refactor your code:
πŸ‘Œ Simplified (Non-Switch) Code
The easies and most straightforward way to write your code is like this:
const myname = 'JOKA';
const dhaba = false;
if ('ROKA' === myname || 'MOKA' === myname || 'TOKA' === myname) {
alert('EEE');
} else if (myname === 'KOKA' && true && true) {
alert('FEEE');
} else {
alert(dhaba ? 'TEEEE' : 'CHEEE');
}
Note how the redundant checks have been removed and the last if - else block have been replaced with a ternary operator.
It is possible that your code is not exactly like the example you provided or that it changes overtime. In that case, you may consider other options other than the above simplified code.
πŸ‘‰ Checking multiple matches from a single variable using Array.prototype.indexOf()
However, you may have a lot more elements to check in the first if. In that case you could use an Array and Array.prototype.indexOf() to check if there's any match inside it (it will return -1 if there isn't any):
const myname = 'JOKA';
const dhaba = false;
if (['ROKA', 'MOKA', 'TOKA'].indexOf(myname) !== -1) {
alert('EEE');
} else if (myname === 'KOKA' && true && true) {
alert('FEEE');
} else {
alert(dhaba ? 'TEEEE' : 'CHEEE');
}
πŸ‘‰ N Input - Output (String) Pairs + Complex Default with Switch
It is also possible that you have multiple myname values that map to multiple alert() params, so you may feel tempted to write something like this:
const myname = 'JOKA';
const dhaba = false;
switch(myname) {
case 'XXX-1': alert('YYY-1'); break;
case 'XXX-2': alert('YYY-2'); break;
...
case 'XXX-N': alert('YYY-N'); break;
default:
if (myname === 'KOKA' && true && true) {
alert('FEEE');
} else {
alert(dhaba ? 'TEEEE' : 'CHEEE');
}
}
While this is fine and, actually, I think it is cleaner and less error-prone than checking an additional condition inside a case block, as you did in your example, and based on that do something and break or let the next block execute, I would advise you to consider using object literal lookups instead.
πŸ‘‰ N Input - Output (String) Pairs + Complex Default with Object Literals Lookups πŸ”Ž
There are multiple advantages to use them: better readability, easier debugging, maintainability, concision (no need to add break, for example)... I think the most important one for you, as you added the tag performance in your question, is that it is more performant.
This is because while the switch has to evaluate each case condition until it fins a break, so their order matters, the object lookup is just a hash table lookup, that is, O(1).
With this in mind, we could refactor the last example like this:
const myname = 'JOKA';
const dhaba = false;
const output = {
'XXX-1': 'YYY-1',
'XXX-2': 'YYY-2',
...
'XXX-N': 'YYY-N',
}[myname];
// Note output will be undefined if there isn't a match, so the first if
// will be evaluated to false in that scenario:
if (output) {
alert(output);
} else if (myname === 'KOKA' && true && true) {
alert('FEEE');
} else {
alert(dhaba ? 'TEEEE' : 'CHEEE');
}
πŸ‘‰ N Input - Output (String) Pairs + Single-Value Default with Object Literals Lookups πŸ”Ž and || (or)Β Operator
Also, note that if your default were just using another value inside the if, you could do that with a simple || operator:
const myname = 'JOKA';
const output = {
'XXX-1': 'YYY-1',
'XXX-2': 'YYY-2',
...
'XXX-N': 'YYY-N',
}[myname] || 'DEFAULT OUTPUT';
alert(output);
πŸ‘‰ N Input - Output (Arbitrary Code) Pairs with Object Literals Lookups πŸ”Ž
Note you could also execute arbitrary code for each case in your objects using functions or arrow functions:
const myname = 'JOKA';
const output = {
'XXX-1': () => { /* Do something... */ },
'XXX-2': () => { /* Do something... */ },
...
'XXX-N': () => { /* Do something... */ },
}[myname]();
...
Note that you could declare those functions above the object declaration and share them across multiple keys that should have the same behaviour:
const myname = 'JOKA';
const f1 = () => { /* Do something 1... */ };
const output = {
'XXX-1': f1,
'XXX-2': f1,
...
'XXX-N': () => { /* Do something... */ },
}[myname]();
...
For more on replace switchs with object literal lookups, take a look at this post: https://toddmotto.com/deprecating-the-switch-statement-for-object-literals

Related

simplify if else statement

I have some functionality dependent on many conditions. All variables in conditional statements are boolean variables and the code is the following and I don't like it:
if (userHasMoreThanOneMarket && isOnlyMarketSelected || !userHasMoreThanOneMarket && userHasMoreThanOneAgency) {
if (isOnlyAgencySelected) {
//do case 1
} else if (noAgencySelected && isOnlyMarketSelected) {
//do case 2
}
}
Is there a way to make it more understandable and nice?
That's about as concise as you're going to get with JavaScript. I suppose if you really wanted to, you could create variables to store your binary options:
var multiMarketOneSelected = userHasMoreThanOneMarket && isOnlyMarketSelected;
var singleMarketMultiAgency = !userHasMoreThanOneMarket && userHasMoreThanOneAgency;
if (multiMarketOneSelected || singleMarketMultiAgency) {
if (isOnlyAgencySelected) {
//do case 1
} else if (noAgencySelected && isOnlyMarketSelected) {
//do case 2
}
}
Though I don't really know if you gain much readability from that.
Your code seems fine, but if you don't like it you could do something like this (note that the only improvement here is style, if you like it better):
function check(){
return {
valid: userHasMoreThanOneMarket && isOnlyMarketSelected || !userHasMoreThanOneMarket && userHasMoreThanOneAgency,
case: [
isOnlyAgencySelected,
noAgencySelected && isOnlyMarketSelected
]
};
}
var conditions = check();
if (conditions.valid) {
if (conditions.case[0]) {
//do case 1
} else if (conditions.case[1]) {
//do case 2
}
}
Some things I would try to make the code more readable:
Initialise the variables in a way that you don't have to negate them again. So !userHasMoreThanOneMarket becomes userHasOneMarket
isOnlyMarketSelected sounds redundant to me. And you are checking it in the outer if-clause and the inner again.
You probably have a lot of code above this code snippet to initialise and set all this boolean values. Try return; statements after each variable to get rid of if-conditions.
I hope this helps.

How to reduce "if statement" conditions? [reduce the conditions inside the if statement]

after days of hard thinking i choose to ask that question. I have if statement with multiple conditions:
//var current is array of arrays of integers
if((current[rot][0] + x)<blocks.length
&& (current[rot][1] + x)<blocks.length
&& (current[rot][2] + x)<blocks.length
&& (current[rot][3] + x)<blocks.length
&& !$(blocks[current[rot][0]+x]).hasClass("blockLand")
&& !$(blocks[current[rot][1]+x]).hasClass("blockLand")
&& !$(blocks[current[rot][2]+x]).hasClass("blockLand")
&& !$(blocks[current[rot][3]+x]).hasClass("blockLand"))
{
//something to happen here ONCE!
}
Because i want something inside to happen just once i think i cant use for loop.
So my question is: is there a possible way to reduce the conditions number? and how?
P.S.: Yes i figured out that i can use flag (true/false) inside and do my stuff outside this if, in another if - but i think that not always gonna work, because for every loop the flag will be different.
var b = true;
for (var i = 0; i <= 3; i++) {
// In two lines for being clear, but it's possible just in one
b = b && (current[rot][i] + x)<blocks.length
b = b && !$(blocks[current[rot][i]+x]).hasClass("blockLand");
// You could speed it up this way.
if(!b) break;
}
if (b) {
//something to happen here ONCE!
}
I think I understand what you are asking but let me know if there is anything else I can do.
JavaScript has a ternary (conditional operator) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator
This operator allows you to assign true/false values based on an internal if/else condition.
Here is some code for you to explain this...
window.onload = function() {
var one = 1;
var two = 2;
console.log(one > two ? "greater" : "not greater");
};
You can also use a Switch statement which you can read about here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch.
Here is an example of a switch statement.
window.onload = function() {
var string = "testing this out";
switch (string) {
case "testing this out":
console.log('testing this out found in condition one');
break;
case "testing":
console.log('found testing');
break;
default:
console.log('not found');
break;
}
};
Let me know if I can improve this.

Javascript: change test() method name

Okay, I am making a text based game and I have been using the switch command like so
switch(true){
case /run/.test(g1):
prompt("run");
break;
}
the question I have is can I can I change the name of the test() method like so
switch(true){
case /run/.act(g1):
prompt("run");
break;
}
I thought if I created a function like so it would work
function act(str){
test(str);
return;
}
but it didn't... any help would be nice.
edit: fixed switch statements
So /run/ is a regex object. It has a method called test. It doesn't have a method act, so hence you can't call act() on it.
You'll need to use prototypes:
switch{
case /run/.act(g1):
prompt("run");
break;
}
RegExp.prototype.act = function (str) {
return this.test(str);
}
Here's an explanation of Prototypes.
If you really need to do this (please see What is the XY problem?) then you can add a prototype to the RegExp object.
var regex = /fo+/;
var str = 'fooooooooooooooo';
regex.test(str); //true
RegExp.prototype.act = function(str) {
if (str == 'fooooooo' && this.test(str)) {
return true;
} else {
return false;
}
}
regex.act(str); //false (str is not 'fooooooo')
Likewise, you can make an alias (but please, don't - it works fine as it is) by simply returning this.test():
RegExp.prototype.act = function(str) {
return this.test(str);
}

Jasmine expect logic (expect A OR B)

I need to set the test to succeed if one of the two expectations is met:
expect(mySpy.mostRecentCall.args[0]).toEqual(jasmine.any(Number));
expect(mySpy.mostRecentCall.args[0]).toEqual(false);
I expected it to look like this:
expect(mySpy.mostRecentCall.args[0]).toEqual(jasmine.any(Number)).or.toEqual(false);
Is there anything I missed in the docs or do I have to write my own matcher?
Add multiple comparable strings into an array and then compare. Reverse the order of comparison.
expect(["New", "In Progress"]).toContain(Status);
This is an old question, but in case anyone is still looking I have another answer.
How about building the logical OR expression and just expecting that? Like this:
var argIsANumber = !isNaN(mySpy.mostRecentCall.args[0]);
var argIsBooleanFalse = (mySpy.mostRecentCall.args[0] === false);
expect( argIsANumber || argIsBooleanFalse ).toBe(true);
This way, you can explicitly test/expect the OR condition, and you just need to use Jasmine to test for a Boolean match/mismatch. Will work in Jasmine 1 or Jasmine 2 :)
Note: This solution contains syntax for versions prior to Jasmine v2.0.
For more information on custom matchers now, see: https://jasmine.github.io/2.0/custom_matcher.html
Matchers.js works with a single 'result modifier' only - not:
core/Spec.js:
jasmine.Spec.prototype.expect = function(actual) {
var positive = new (this.getMatchersClass_())(this.env, actual, this);
positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
return positive;
core/Matchers.js:
jasmine.Matchers = function(env, actual, spec, opt_isNot) {
...
this.isNot = opt_isNot || false;
}
...
jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
return function() {
...
if (this.isNot) {
result = !result;
}
}
}
So it looks like you indeed need to write your own matcher (from within a before or it bloc for correct this). For example:
this.addMatchers({
toBeAnyOf: function(expecteds) {
var result = false;
for (var i = 0, l = expecteds.length; i < l; i++) {
if (this.actual === expecteds[i]) {
result = true;
break;
}
}
return result;
}
});
You can take the comparison out of the expect statement to gain full use of comparison operators.
let expectResult = (typeof(await varA) == "number" || typeof(await varA) == "object" );
expect (expectResult).toBe(true);

Call break in nested if statements

I have the following situation:
IF condition THEN
IF condition THEN
sequence 1
ELSE
break //?
ENDIF
ELSE
sequence 3
ENDIF
What is the result of the break statement? Does it break the outer if statement? Because this is what I actually need.
If you label the if statement you can use break.
breakme: if (condition) {
// Do stuff
if (condition2){
// do stuff
} else {
break breakme;
}
// Do more stuff
}
You can even label and break plain blocks.
breakme: {
// Do stuff
if (condition){
// do stuff
} else {
break breakme;
}
// Do more stuff
}
It's not a commonly used pattern though, so might confuse people and possibly won't be optimised by compliers. It might be better to use a function and return, or better arrange the conditions.
( function() {
// Do stuff
if ( condition1 ) {
// Do stuff
} else {
return;
}
// Do other stuff
}() );
no it doesnt. break is for loops, not ifs.
nested if statements are just terrible. If you can avoid them, avoid them. Can you rewrite your code to be something like
if (c1 && c2) {
//sequence 1
} else if (c3 && c2) {
// sequence 3
}
that way you don't need any control logic to 'break out' of the loop.
But there is switch-case :)
switch (true) {
case true:
console.log("Yes, its ture :) Break from the switch-case");
break;
case false:
console.log("Nope, but if the condition was set to false this would be used and then break");
break;
default:
console.log("If all else fails");
break;
}
In the most languages, break does only cancel loops like for, while etc.
To make multiple checking statements more readable (and avoid nested ifs):
var tmp = 'Test[some.email#somewhereouttherebutnothere.com]';
var posStartEmail = undefined;
var posEndEmail = undefined;
var email = undefined;
do {
if (tmp.toLowerCase().substring(0,4) !== 'test') { break; }
posStartEmail = tmp.toLowerCase().substring(4).indexOf('[');
posEndEmail = tmp.toLowerCase().substring(4).indexOf(']');
if (posStartEmail === -1 || posEndEmail === -1) { break; }
email = tmp.substring(posStartEmail+1+4,posEndEmail);
if (email.indexOf('#') === -1) { break; }
// all checks are done - do what you intend to do
alert ('All checks are ok')
break; // the most important break of them all
} while(true);
Javascript will throw an exception if you attempt to use a break; statement inside an if else. It is used mainly for loops. You can "break" out of an if else statement with a condition, which does not make sense to include a "break" statement.
JSFiddle
Actually there is no c3 in the sample code in the original question. So the if would be more properly
if (c1 && c2) {
//sequence 1
} else if (!c1 && !c2) {
// sequence 3
}
I had a similar problem today and found refactoring the conditional logic into a separate function to help.
I find it more readable than the labels and people are more comfortable with return than break. Inline functions are similar but the indentation can get a bit confusing.
In your case it would look like this.
function doThing() {
checkConditions();
// Rest of the code here
}
function checkConditions() {
if (c1) {
if (c2) {
return do1();
else {
return;
}
} else {
return do3();
}
}
Just remove the break. since it is already inside first if it will not execute else. It will exit anyway.
You need that it breaks the outer if statement. Why do you use second else?
IF condition THEN
IF condition THEN
sequence 1
// ELSE sequence 4
// break //?
// ENDIF
ELSE
sequence 3
ENDIF
sequence 4

Categories