I'm wondering if it would be better to use a switch statement in my specific case.
I'm writing an Alexa Custom Skill, and I need to "redirect" to the appropriate intent depending on the available information (aka slots). Below is what I have currently (using if/else):
if (_type === "IntentRequest") {
this.handler.state = states.START;
if (_slots.indexOf("address") != -1) {
this.emitWithState("GoingToAddressIntent");
} else if (_slots.indexOf("place") != -1) {
this.emitWithState("GoingToPlaceIntent");
} else if (_slots.indexOf("type") != -1) {
this.emitWithState("GoingToTypeIntent");
} else if (_slots.indexOf("purpose") != -1) {
this.emitWithState("GoingToPurposeIntent");
} else {
this.emit("Unhandled");
}
}
I expect _slots to be an array of any permutations of the four elements, [ "address", "place", "type", "purpose" ]. Therefore, it could be anything from [ "address" ] to [ "place", "purpose" ] to etc. etc., but always in the same order (e.g. [ "purpose", "address" ] would never happen).
The order of the comparisons matters because there is a "hierarchy" of information; so if the "address" slot is present, I have to emit the "GoingToAddressIntent" regardless of what other slots are available. Given this requirement, I thought using a switch statement maybe more straightforward and readable despite having to have a few extra lines of code to "convert" the array of strings to an array of booleans. It clearly lays out the hierarchy & make sure they are evaluated in order. I could do:
if (_type === "IntentRequest") {
this.handler.state = states.START;
slots = [
_slots.indexOf("address") != -1,
_slots.indexOf("place") != -1,
_slots.indexOf("type") != -1,
_slots.indexOf("purpose") != -1
]
switch(slots.indexOf(true)) {
case 0:
this.emitWithState("GoingToAddressIntent");
break;
case 1:
this.emitWithState("GoingToAddressIntent");
break;
case 2:
this.emitWithState("GoingToTypeIntent");
break;
case 3:
this.emitWithState("GoingToPurposeIntent");
break;
default:
this.emit("Unhandled");
}
}
... in which case I have an extra line to define the array of booleans, use indexOf() to get the index of the first occurrence of a true literal (because all 4 slots are always in the order of hierarchy), and run it through the switch statement. However I wanted ask experts on their ideas of what best programming practice is in this case and the reasoning behind it because I want this to become a long-term project that is maintainable, and also I believe I can learn something from their insights.
Please leave a comment if you think this should be migrated to another community on SE, but from my research (although 3 years old) I believe this should be fine (I'm just not 100% confident on this).
If they're always in the order of precedence in _slots, maybe you could make a hash map to the state you're going to emit...
const map = {
address: "GoingToAddressIntent",
place: "GoingToPlaceIntent",
type: "GoingToTypeIntent",
purpose: "GoingToPurposeIntent"
};
const state = map[_slots[0]];
if (state) {
this.emitWithState(state);
} else {
this.emit("Unhandled");
}
I wouldn't go with your example of the switch statement. People could understand what you're attempting to do, but it does seem pretty convoluted. I use switch statements pretty liberally, mostly in backend code, and I think it could work fine here. A group of if/else is fine too, since there's only 4 cases you need to work through. Lets roll with the switch statement since that's what you're asking about.
Based on your explanation, the order is always going to be the same, although the first value you get may be different. So the solution would be to simply grab the first value, and switch over that.
if (!!slots.length) {
var keyword = slots[0];
switch (keyword) {
case 'address':
this.emitWithState("GoingToAddressIntent");
break;
case 'place':
this.emitWithState("GoingToPlaceIntent");
break;
case 'type':
this.emitWithState("GoingToTypeIntent");
break;
case 'purpose':
this.emitWithState("GoingToPurposeIntent");
break;
default:
this.emit('Unhandled'); // I typically throw an exception here
}
}
Related
In this action I am trying to say...
if VS_WRNG is Blank and if VS_WRNG is DSC or DSQ then set the two items
but when I click on VS_WRNG in my web app and select DSC it didn't autofill the BOB_TYPE answer with LIN.
Did I screw up the code or is it the ! not working like that and do I need to do a switch (true) & case (VS_WRNG != "")?
//Set BoB Type [TEMPORARY]
switch (!VS_WRNG) {
case "":
switch (VS_WRNG) {
case "DSC":
case "DSQ":
switch (VS_BTYP) {
case "":
setValue('BOB_TYPE',"LIN");
VS_BTYP = "LIN";
break;
}
break;
}
break;
}
Thanks
The primary issue you're having is that !VS_WRNG always results in a boolean. It can never result in an empty string "". So the case "": will never run. But even if it did, your code still won't work:
In this action I am trying to say... if VS_WRNG is Blank and if VS_WRNG is DSC or DSQ then set the two items
VS_WRNG cannot be simultaneously Blank and DSC or DSQ. You need to rethink your logic - the requirements are incompatible right now and quite illogical.
This is also quite an abuse of switch statements. If your switch statement has one case, it's an if statement. Just use an if statement like this:
if(VS_WRNG === "") { ... }
Your middle switch statement can be implemented also with an if statement, since you've written it to just check for two conditions:
if(VS_WRNG == "DSC" || VS_WRNG == "DSQ") { ... }
it happened to me multiple times that I face situation where I wasnt sure how I should stop function when I know result.
Just for example lets imagine we have object category which looks like:
{
"name": "CategoryName",
"subCategoriess": [{
"name": "Sub1",
"value": true
}, {
"name": "Sub2",
"value": false
}]
}
and we want to iterate over it until we found value === true
Clasic option (let's call it: Option 1)which i found in most documentation is:
function myFunction(category){
var ret = false;
for (var i=0; i <category.subCategoriess.length; i++)
{
ret = category.subCategories[i].value;
if (ret)
break;
}
return ret;
}
I alternativly saw something like this (let's call it: Option 2):
function myFunction(category){
for (var i=0; i <category.subCategoriess.length; i++)
{
if (category.subCategories[i].value) return true;
}
return false;
}
So let's compare this two options, i consider my self as junior so I may be wrong really easy so please do not hesitate to tell me that
Option 1 - Advantage of this option is that even if your code is more complicated you always know which return actualy "return" the function and I expect it may be easier for debug for many of us.
Option 2 - Advantage of this option is that it should use less performance becuase it doesnt need aditional variabile (ret) + we basicly avoid atleast one step where we break a for loop and going to actual return statment
In this particular example the differenc between each option is something really close to 0. But code can get more complicated so may I ask you guys what option is best practice (or if I miss something please add your option) and why it should be used like this?
There is more then just two options and it comes down to this:
Which do you (or your team) find is more comprehensible with the lowest cognitive load to read?
Some find procedural code easier. Some attempt to lower temp variables by returning early. Still some find a functional approach better:
return subject.collection.some(function (item) {
return !!item.value;
});
So it comes down to your team and your own cognitive preferences. The true take away is be consistent in your project. If you start with one style and find you like a new style note this in the README and maybe convert old styles as you see them.
The only answer to this question is: it depends (on code complexity)
Generally speaking, multiple return statement could make debugging a bit harder, but sometimes it is worth it.
In your example I'd probably go for the second option if I'd be sure that this method would only return true/false value.
But if I'd like to extend it's functionality in the future (fetch the record which fulfills the condition or get all the values that fulfill it) it may be easier to go for the first option.
I'd say option 2 because you don't need any intermediary variable to store the result, and the intent is clearer since you return immediately as soon as you can.
You can also improve legibility and performance (a bit) by storing each item as you iterate.
for (var i = 0, item; item = category.subCategoriess[i]; i++) {
if (item.value) return true;
}
return false;
How about Option 3?
Using Array.some gives you the best of both worlds as it doesn't require a break or multiple return statements.
var category = {
"name": "CategoryName",
"subCategoriess": [{
"name": "Sub1",
"value": true
}, {
"name": "Sub2",
"value": false
}]
}
function myFunction(category){
var result = category.subCategoriess.some(function(subCategory) {
return subCategory.value;
});
return result;
}
console.log(myFunction(category))
First time writing Javascript. I just would like know if there is a shorter way of writing this:
<p id="demo"></p>
<script>
function myFunction() {
var letter = document.getElementById("myInput").value;
var text;
if (letter === "5544") {
text = "Abar, Marlon 1,800";
} else if (letter === "5545") {
text = "Pia, Darla 1,800";
} else if (letter === "5546") {
text = "Salazar, Alex 1,500";
//etc...
} else {
text = "Incorrect Account Number";
}
document.getElementById("demo").innerHTML = text;
}
</script>
Tried map but I couldn't get it to work.
There isn't really a shorter way to write an if statement in that way (which I will assume is what you're asking). However, there could be a few different ways to write this depending on how many things you want to check.
Use a Switch statement
There is a cleaner way when dealing with multiple cases that letter could be.
This would be a switch statement and it would look like this:
var text;
switch (letter) {
case "5544":
text = "Abar, Marlon 1,800";
break;
case "5545":
text = "Pia, Darla 1,800";
break;
// more cases
default:
text = "Incorrect Account Number";
break;
}
This reads a little better than an if else statement in some cases. The default keyword here acts as your else clause in an if else statement. The case acts as your different if statements if you will.
Essentially, the switch statement above will fall through each of the cases it defines until it finds a case that matches letter (such as "5544"). If none matches, it hits the default case. The break keyword at the end of each case stops things from falling through to the next defined case once a match is found.
This method could get cumbersome with more than 6 or 7 cases.
Create an object and look up the value
Now, a shorter way to get the value you want could be to define an object and get the value based on what has been entered like so:
var letter = document.getElementById('selector').value;
var obj = {
'5544': 'Abar, Marlon 1,800'
};
if (letter in obj) {
// do something if found
}
else {
// do something if not found
}
This could be an easy way to get a value if you have many values to check.
Other thoughts
As a side note to all of this, there are short hand if statements called ternary statements which you can find here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator ... However, I would not recommend nesting these as it becomes very complicated and not very readable.
Conclusion
So, to reiterate the answer to your question: No, there isn't really a shorter way to write an if else statement with many values. You can use a switch statement to make it cleaner. Use the object lookup method if you have many values you would like to check.
JavaScript has object (map) literals. Use them for terse code. In your final application you'll get the data for the map from someplace else and not code it directly into your website, but if you did, it would look like this:
document.getElementById( "demo" ).innerHTML = {
"5544" : "Abar, Marlon 1,800",
"5445" : "Pia, Darla 1,800",
...
}[ document.getElementById( "myInput" ).value ];
you can use switch for a long if - else -if ladder:
switch(expression) {
case n:
code block
break;
case n:
code block
break;
default:
default code block
}
This is how it works:
1)The switch expression is evaluated once.
2)The value of the expression is compared with the values of each case.
3)If there is a match, the associated block of code is executed.
if you need basic tutorials in java script then you should try w3 schools.
I have a Javascript-based bot for a Xat chatroom which also acts as an AI. I've recently decided to redo the AI part of it due to it becoming an absolutely massive chain of else if statements, becoming nearly impossible to work with.
I did some research and came up with a new idea of how to handle responses. I'll give you the code segment first:
function msgSwitch(id,msgRes) {
var botResponse = [];
switch (msgRes) {
case (msgRes.indexOf("hi") !=-1):
botResponse.push("HELLO. ");
case (msgRes.indexOf("how are you") !=-1):
botResponse.push("I AM FINE. ")
case (msgRes.indexOf("do you like pie") !=-1):
botResponse.push("I CAN'T EAT. THANKS, ASSHAT. ")
default:
respond (botResponse);
spamCount(id);
break;
}
}
The idea here is to check msgRes (the user's input) and see how many cases it matches. Then for each match, it'll push the response into the botResponse array, then at the end, it'll reply with all the messages in that array.
Example
User Msg: Hi! How are you?
msgRes: hi how are you
Bot Matches:
hi > pushes HELLO. to array
how are you > pushes I AM FINE. to array
Bot Responds: HELLO. I AM FINE.
This in turn saves me the trouble of having to write an if for each possible combination.
However, after looking into it some more, I'm not sure if it's possible use indexOf inside of a switch. Does anyone know of a way around this or have a better idea for handling responses in the same manner?
EDIT:
To Avoid the XY Problem (To clarify my problem)
I need a clean alternative to using a massive chain of else if statements. There are going to be hundreds of word segments that the bot will respond to. Without the ability for it to keep searching for matches, I'd have to write a new else if for every combination.
I'm hoping for a way to have it scan through every statement for a match, then combine the response for each match together into a single string.
EDIT 2:
I should also add that this is being ran on Tampermonkey and not a website.
you just need to compare to true instead of msgRes (since cases use === comparison), and use break to prevent the annoying fall-though of the switch behavior:
function msgSwitch(id,msgRes) {
var botResponse = [];
switch (true) {
case (msgRes.indexOf("hi") !=-1):
botResponse.push("HELLO. "); break;
case (msgRes.indexOf("how are you") !=-1):
botResponse.push("I AM FINE. "); break;
case (msgRes.indexOf("do you like pie") !=-1):
botResponse.push("I CAN'T EAT. THANKS, ASSHAT. "); break;
default:
respond (botResponse);
spamCount(id);
break;
}
}
This is a perfectly valid logical forking pattern, known as an "overloaded switch". A lot of folks might not realize that each case: is an expression, not just a value, so you could even put an IIFE in there if needed...
My two cents for the gist of what you're trying to do:
function msgSwitch(id, msgRes) {
var seed = {'hi': 'HELLO. ', 'how are you': 'I AM FINE'};
var botResponse = [];
for (var key in seed) {
if (msgRes.indexOf(key) !== -1) {
botResponse.push(seed[key]);
}
}
}
In my opinion it is easier to change this program as you only have to edit the seed if you have more responses in the future. You can even stash the seed on some json file and read it (via ajax) so the program does not need to be changed if there are additional messages.
Here is a snippet of JavaScript code from a tutorial I was working with. I don’t understand why it doesn’t end with a final else clause; I thought that was a rule.
var curScene = 0;
function changeScene(decision) {
var message = "";
if(curScene == 1) {
message = " welcome";
} else if (curScene == 2) {
message = " this is scene two";
} else if (curScene == 3) {
message = " this is scene three";
}
document.getElementById("sceneimg").src = "scene" + curScene + ".png";
if(message != ""){
alert(message);
}
}
I thought it was always supposed to end with an "else"?
The else block is optional. You can have if without else.
For the same reason as why you can have just a single if:
if( /*condition*/ ) {
//some code
}
//other stuff
Consider 3 Scenario
Scenario 1: Boolean condition
if (condition) {}
else {}
Specifying a condition as else if would be redundant, and it's really obvious to the reader what the code does. There is no argument for using else if in this case.
Scenario 2: Infinite states
Here we are interested in testing for conditions A and B (and so on), and we may or may not be interested in what happens if none of them holds:
if (conditionA) {}
else if (conditionB) {}
else {} // this might be missing as it is in your case
The important point here is that there isn't a finite number of mutually-exclusive states, for example: conditionA might be num % 2 == 0 and conditionB might be num % 3 == 0.
I think it's natural and desirable to use a reasonable amount of branches here; if the branches become too many this might be an indication that some judicious use of OO design would result in great maintainability improvements.
Scenario 3: Finite states
This is the middle ground between the first two cases: the number of states is finite but more than two. Testing for the values of an enum-like type is the archetypal example:
if (var == CONSTANT_FOO) {}
else if (var == CONSTANT_BAR) {} // either this,
else {} // or this might be missing
In such cases using a switch is probably better because it immediately communicates to the reader that the number of states is finite and gives a strong hint as to where a list of all possible states might be found (in this example, constants starting with CONSTANT_). My personal criteria is the number of states I 'm testing against: if it's only one (no else if) I 'll use an if; otherwise, a switch. In any case, I won't write an else if in this scenario.
Adding else as an empty catch-errors block
This is directly related to scenario #2 above. Unless the possible states are finite and known at compile time, you can't say that "in any other case" means that an error occurred. Seeing as in scenario #2 a switch would feel more natural, I feel that using else this way has a bad code smell.
Use a switch with a default branch instead. It will communicate your intent much more clearly:
switch(direction) {
case 'up': break;
case 'down': break;
default: // put error handling here if you want
}
This might be a bit more verbose, but it's clear to the reader how the code is expected to function. In my opinion, an empty else block would look unnatural and puzzling here.
It doesn't have to, for the same reason an if on its own doesn't require an else.
Usually it's a good idea to have one, as a sort of "catch-all" situation, but the above code could be written as:
switch(curScene) {
case 1: message = " welcome"; break;
case 2: message = " this is scene two"; break;
case 3: message = " this is scene three"; break;
}
In the above code, I could also add:
default: message = " invalid curScene value"; break;
But it's completely optional to do so. It depends on how reliable the curScene variable is whether or not I personally would add it in.
Not having an else clause is fine syntactically. MDN Documentation Basically the second if becomes the body of the else, see the section on "how it would look like if the nesting were properly indented".
As to whether it's bad practice I think that depends on intent. By not explicitly defining the final else clause, you might end up with a bug where a condition you didn't cover comes through. Consider this:
if(myVariable > 0) {
doSomething();
} else if(myVariable < 0) {
doSomethingElse();
}
Nothing happens if myVariable is 0. It would be hard to see if you were just glancing through the code. I would say if you run into this pattern it would be a code smell, something might be wrong, but it could be fine.
The same logic could always be expressed with nested if statements. I would go with whatever is more readable.
else is a default case for the if statement.
If there is no else then if none of the conditions in the if or else if cases are met than the if statment will do nothing.
Usually it is good practice to have a default case but there are a lot of times where it is not necessary and thus excluded from the code.
In this case, if the curScene was anything other than 1, 2, 3 then the else statment would be used, but since there is no processing to be done on other cases the coder has not included an else.
yes, always have a else is VERY GOOD habit(when using with if-elseif). sometimes people might even write this:
if(curScene == 1) {
message =" welcome";
else if (curScene == 2) {
message = " this is scene two";
}
else if (curScene == 3) {
message = " this is scene three";
} else {
// empty.
}
to tell people there is indeed nothing to do in the else.
change the if condition for answer validation if(answer==100) to if(answer===100)
it is working fine now...