How to write Cleaner Nested Conditions in JavaScript - javascript

Using if statement we will get so much of nesting and if statement readability will reduce if the code size increases.
If Statement example:
if (item) {
if (item === 'palladium') {
printPalladiumPrice();
} else if (item === 'silver') {
printSilverPrice();
} else if (item === 'gold') {
printGoldPrice();
} else if (item === 'platinum') {
printPlatinumPrice();
} else if (item === 'diamond') {
printDiamonPrice();
} else {
printAllItemPrice();
}
}
The same problem we will face with the Switch statement. If the size increases the Switch Case readability will reduce as shown below.
Switch Case example:
switch (item) {
case 'palladium':
printPalladiumPrice();
break;
case 'silver':
printSilverPrice();
break;
case 'gold':
printGoldPrice();
break;
case 'platinum':
printPlatinumPrice();
break;
case 'diamond':
printDiamondPrice();
break;
default:
printAllItemPrice();
}
If we use JSON OBJECT our conditions will reduce to one single if statement and code will be much readable. Below is the example to write Cleaner Nested Conditions using the JSON Object.
JSON Object example:-
const itemObj = {
'palladium': printPalladiumPrice,
'silver': printSilverPrice,
'gold': printGoldPrice,
'platinum': printPlatinumPrice,
'diamond': printDiamondPrice,
};
if (item in itemObj) {
itemObj[item]();
}
Our conditions are now reduced to one single if and code is much readable.

Related

Is there a better way to write this switch in 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

Switch is not properly working in javascript

I have a switch statement which has several cases. These cases compare values and assign a text to a variable. But when I try to execute this switch, it always executes the default case. But my condition is true.. Why?
Here is my value
Apartment
Here is my code
var rental_cat = $('#rentals_type').val();
alert(rental_cat);
var rental_type = "";
switch (rental_cat) {
case (rental_cat == "Apartment"):
rental_type='daily';
alert(rental_type);
break;
case (rental_cat == "Office"):
rental_type='work_daily';
alert(rental_type);
break;
default:
rental_type='other';
alert(rental_type);
break;
}
When I execute this switch, it always gives me "other"
Remove the conditional expression inside the "case" clause.
Try this:
var rental_cat = $('#rentals_type').val();
alert(rental_cat);
var rental_type = "";
switch (rental_cat) {
case "Apartment":
rental_type='daily';
alert(rental_type);
break;
case "Office":
rental_type='work_daily';
alert(rental_type);
break;
default:
rental_type='other';
alert(rental_type);
break;
}
switch (rental_cat) {
case (rental_cat == "Apartment"):
is equivalent to
switch (rental_cat) {
case true:
which in turn is equivalent to
if (rental_cat === true)
You don't put a condition in the case, the condition is created as an equality check between the switch and the cases, so it should be like this instead:
switch (rental_cat) {
case "Apartment":
a switch is not the right structure to deal with this problem. Here I'd recommend a map:
var rentalTypesByCat = {
DEFAULT: "other",
"Apartement": "daily",
"Office": "work_daily",
}
var rental_cat = $('#rentals_type').val();
console.log("rental_cat:", rental_cat);
var rental_type = rentalTypesByCat[rental_cat] || rentalTypesByCat.DEFAULT;
console.log("rental_type:", rental_type);
or if you need it a bit more explicit (for example because some of your mapped values may be falsy themselves):
var rental_type = rentalTypesByCat[rental_cat in rentalTypesByCat? rental_cat: "DEFAULT"];
console.log("rental_type:", rental_type);

Javascript coding quiz not adding up

I'm trying to do a simple quiz where it should sum the correct answers and incorrect answers. The thing is although I put two out of three correct answers, I keep getting the same result for the correct and incorrect array: 0. So there must be something wrong at the end, in the evaluate function. Thanks in advance
var responsesArray= [];
var correct=[];
var incorrect= [];
function question2() {
var firstQuestion = prompt('Does null === 0 ? (Yes or No)')
// why do you need to convert the answer to lowercase?
if (firstQuestion.toLowerCase() === 'yes') {
firstQuestion = true
} else if (firstQuestion.toLowerCase() === 'no') {
firstQuestion = false
} else {
// what if the user writes something other than yes or no?
// they will have to answer the question again
alert("Please answer either Yes or No");
return question2();
}
responsesArray.push(firstQuestion); // add the true or false value to the responses array
}
question2();
function question3() {
var js = prompt('What was the original name for JavaScript: Java, LiveScript, JavaLive, or ScriptyScript?');
js = js.toLowerCase();
switch (js) {
// your own answers
case "livescript":
console.log("Correct!");
break;
case "Java":
console.log("wrong");
break;
case "JavaLive":
console.log("wrong");
break;
case "ScriptyScript":
console.log("wrong");
break;
default:
console.log("Sorry the answer is LiveScript");
}
responsesArray.push(js);
var mine = prompt('What coding language is exclusively related to the back-end: Ruby, JavaScript, HTML?');
mine= mine.toLowerCase();
switch (mine) {
// your own answers
case "ruby":
console.log("Yeah!");
break;
case "html":
console.log("ouuu I'm sorry for you");
break;
case "javascript":
console.log("Yeah but so so");
break;
}
responsesArray.push(mine);
}
question3();
function evaluate(responsesArray)
{
for (var i = 0; i < responsesArray.length; i++)
{
if (responsesArray[i] === true|| "livescript" || "ruby")
{
correct++;
} else{
if (responsesArray[i] !== true|| "livescript" || "ruby") {
incorrect++;
}
}
}
Define an array to store the correct answer and then compare correct and user response and easily can identify whether it is correct or not.
Please check below snippet.
var responsesArray= [];
var correct=0;
var incorrect= 0;
//Correct answer key initialize
var index = 0;
//Initialize array to store correct answer.
var correctAnswers = [];
function question2() {
//Save correct answer.
correctAnswers[index++] = "yes";
var firstQuestion = prompt('Does null === 0 ? (Yes or No)')
// why do you need to convert the answer to lowercase?
if (firstQuestion.toLowerCase() === 'yes') {
console.log("correct");
firstQuestion = 'yes'
} else if (firstQuestion.toLowerCase() === 'no') {
console.log("in-correct");
firstQuestion = 'no'
} else {
// what if the user writes something other than yes or no?
// they will have to answer the question again
alert("Please answer either Yes or No");
return question2();
}
responsesArray.push(firstQuestion); // add the true or false value to the responses array
}
question2();
function question3() {
//Save correct answer.
correctAnswers[index++] = "livescript";
var js = prompt('What was the original name for JavaScript: Java, LiveScript, JavaLive, or ScriptyScript?');
js = js.toLowerCase();
switch (js) {
// your own answers
case "livescript":
console.log("Correct!");
break;
case "Java":
console.log("wrong");
break;
case "JavaLive":
console.log("wrong");
break;
case "ScriptyScript":
console.log("wrong");
break;
default:
console.log("Sorry the answer is LiveScript");
}
responsesArray.push(js);
//Save correct answer.
correctAnswers[index++] = "ruby";
var mine = prompt('What coding language is exclusively related to the back-end: Ruby, JavaScript, HTML?');
mine= mine.toLowerCase();
switch (mine) {
// your own answers
case "ruby":
console.log("Yeah!");
break;
case "html":
console.log("ouuu I'm sorry for you");
break;
case "javascript":
console.log("Yeah but so so");
break;
}
responsesArray.push(mine);
//Call function to evaluate correct or incorrect answer
evaluate(responsesArray,correctAnswers)
}
question3();
function evaluate(responsesArray,correctAnswers)
{
for (var i = 0; i < responsesArray.length; i++)
{
//Match response with correct answer.
if (responsesArray[i] === correctAnswers[i])
{
correct++;
} else{
if (responsesArray[i] !== correctAnswers[i]) {
incorrect++;
}
}
}
alert("Correct : "+correct+" and Incorrect : "+incorrect);
}
The way you test for correct answers is wrong. Instead define an array with the correct answers and verify them as follows:
var correct = incorrect = 0; // better initialise your variables
function evaluate(responsesArray) {
var correctAnswers = [true,"livescript","ruby"];
for (var i = 0; i < responsesArray.length; i++) {
if (responsesArray[i] === correctAnswers[i]) {
correct++;
} else {
incorrect++;
}
}
}
What you had was:
if (responsesArray[i] === true|| "livescript" || "ruby"){
But this means:
if the answer was true, or .... "livescript" is true, or ... "ruby" is true, then
As JavaScript considers strings to be truthy, the if condition would always be true.
Note also that there is no need to do a second if, since the else part is only executed if the first if condition was false, which means you already have filtered for the cases where the answer was wrong.
Finally, your counter variables should be defined before you start incrementing them. It works without this definition, but if one of the two variables is not incremented, it will still be undefined after your call to evaluate. Better always define your variables.

Minimize the use of "Many repeated if statement"

I got a script using TONS of if statement to do a task... But....
Most of them repeated like:
if (parent.getObject()=='apple') {
if (datatype('fruit')){
//do a
parent.setObjectType('fruit');
}
else if (datatype('beverage')){
//do a
parent.setObjectType('beverage');
}
}
else if (parent.getObject()=='banana') {
if (datatype('fruit')){
//do a
parent.setObjectType('fruit');
}
else if (datatype('beverage')){
//do a
parent.setObjectType('beverage');
}
}
else if (parent.getObject()=='coconut') {
if (datatype('fruit')){
//do a
parent.setObjectType('fruit');
}
else if (datatype('beverage')){
//do a
parent.setObjectType('beverage');
}
}
else if (parent.getObject()=='Dewberry') {
if (datatype('fruit')){
//do a
parent.setObjectType('fruit');
}
else if (datatype('beverage')){
//do a
parent.setObjectType('beverage');
}
}
Is it possible for me to minimize the use of those repeated codes?
I don't want to use up all the spaces only with lines of if else statement...
I'm not sure whether your actual code is a bit more diverse, but what you have posted can exactly be replicated by
if (['apple', 'banana', 'coconut', 'Dewberry'].some(function(fruit) {
return parent.getObject()==fruit;
}) {
var type = ['fruit', 'beverage'].find(datatype);
if (type) {
//do a
parent.setObjectType(type);
}
}
(where the find function is something like the following
Array.prototype.find = function(predicate, context) {
for (var i=0; i<this.length; i++)
if (predicate.call(context, this[i], i))
return this[i];
return null;
};
Without seeing more code maybe you could use something like this:
function doTask(a, b) {
var as_string = a + ";" + b;
switch (as_string) {
case "1;1":
//code when a=1 and b=1
break;
case "1;2":
//code when a=1 and b=2
break;
}
}
var a = 1;
var b = 1;
doTask(a, b);
You may or may not be able to reduce if statements depending on your code.
For example if you're adding odd numbers from 0 to n, you an either loop from 0 to n and use if statements to check if the number is odd. However if you happen to know Gauss's rule 1/2*n*(n+1), you can compute this value in one line without any looping or conditional statements (by removing the k(k+1); k even numbers).
You can simply use The switch Statement, it's more efficient in the case you have many possible values:
switch (parent.getObject()) {
case "apple":
switch (datatype) {
case "fruit":
//Task //do task a
//do task b with value 1
break;
case "beverage":
//do task a
//do task b with value 2
break;
}
break;
case "banana":
// Task a with ...
break;
// And put all your possible values
}
You have just to manage the possible vlaues of parent.getObject() and datatype.

Switch Case Regex Test

var path = location.pathname;
switch(path){
case (/\/memberlist/).test(path) :getCSS('url-22.css'); break;
case (/\/register/).test(path): getCSS('url-6.css'); break;
case (/buy-credits/g).test(path): getCSS('url-7.css'); break;
case (/\/?u(\d+)friends$/).test(path): getCSS('url-8.css'); break;
case (/\/privmsg/).test(path): getCSS('url-9.css'); break;
case (/\/?u(\d+)wall$/).test(path): getCSS('url-4.css'); break;
}
function getCSS(url,media){
var a = document.createElement('link');
a.href=url;
a.media= media || 'screen';
a.rel="stylesheet";
return (document.getElementsByTagName('head')[0].appendChild(a));
}
That is my code, and for some reason it's not running the function that should run. For testing purpose we could change var path="/memberlist" and it still won't run. Can someone explain to me why this won't run. Don't really use switch statements
change
switch(path){
to
switch(true){
as you can see in thread I'm reffering to in comment.
None of the answers posted show a correct method to use a RegExp pattern in a switch case so I thought I'd post:
switch (myVar) {
case 'case1':
/...do work
break
case /[a-z]*/.test(myVar) && myVar:
/...regex match, do work
break
}
switch-case doesn't work that way.
regex.test() method returns a boolean (true/false) and you are comparing that against the input string itself which will not be true for any of the case statement.
You need to convert switch-case into multiple if / else if / else block to execute your logic.
Just for the record, the switch case could be rewritten to:
getCSS(
/\/memberlist/).test(path) && 'url-22.css' ||
/\/register/).test(path) && 'url-6.css' ||
/buy-credits/g).test(path) && 'url-7.css' ||
/\/?u(\d+)friends$/) && 'url-8.css' ||
/\/privmsg/).test(path) && 'url-9.css' ||
/\/?u(\d+)wall$/).test(path) && 'url-4.css' ||
'default'
);
Or rewrite getCSS, using a helper object
var path2url = {
css: [
{re: /\/register/, css: 'url-22.css'},
{re: /buy-credits/g, css: 'url-6.css'},
{re: /\/?u(\d+)friends$/, css: 'url-8.css'},
{re: /\/privmsg/, css: 'url-8.css'},
{re: /\/?u(\d+)wall$/, css: 'url-4.css'}
],
getURL: function(path) {
var i = this.css.length;
while (--i) {
if (this.css[i].re.test(path)) {
return this.css[i].css;
}
}
return null; // or something default
}
};
function getCSS(path,media){
var a = document.createElement('link');
a.href= path2url.getURL(path); // <=
a.media= media || 'screen';
a.rel="stylesheet";
return (document.getElementsByTagName('head')[0].appendChild(a));
}
Have a look
switch(str){
case (/(abc|xyz)/.test(str) ? str : false):
// Do some stuff
break;
default:
// Do default stuff
}

Categories