Expression inside switch case statement

JavascriptSwitch StatementCase

Javascript Problem Overview


I'm trying to create a switch statement but I can't seem to be able to use an expression that gets evaluated (rather than a set string/integer). I can easily do this with if statements but case should hopefully be faster.

I'm trying the following

function reward(amount) {
    var $reward = $("#reward");
    switch (amount) {
        case (amount >= 7500 && amount < 10000):
            $reward.text("Play Station 3");
            break;
        case (amount >= 10000 && amount < 15000):
            $reward.text("XBOX 360");
            break;
        case (amount >= 15000):
            $reward.text("iMac");
            break;
        default:
            $reward.text("No reward");
            break;
    }
}

Am i missing something obvious or is this not possible? Google hasn't been friendly in this case.

Any help/pointers appreciated

M

Javascript Solutions


Solution 1 - Javascript

amount is a number, but the expressions in the case clauses only evaluate to booleans; the values will never match.

You could always do

switch (true) {
  case (amount >= 7500 && amount < 10000):
    // Code
    break;
  case (amount >= 10000 && amount < 15000):
    // Code
    break;
  // etc.
}

It works because the value being matched is now the boolean true, so the code under the first case clause with an expression that evaluates to true will be executed.

It’s kinda “tricky”, I guess, but I see nothing wrong with using it. A simple ifelse statement would probably be more concise, and you’d not have to worry about accidental fall-through. But there it is anyway.

Solution 2 - Javascript

@MooGoo's switch (true) will give you a Weird condition error in jsLint, so let's get a little more creative in case that's an issue, and, I think, increase readability a touch.

So we're not evaluating if each case is true or false; we're comparing if that case's value is equal to our switch term. So let's take advantage of that by throwing a shorthand if into our case statement and return our original switch term if the condition's true.

I'm also including a sort of real world example, where you want to have two "defaults" -- one if your term is outside of your "important" range in the positive direction, and another in case you're in the negative direction.

Key phrase: case (x > 0 ? x : null):

"If my term, x, is greater than zero, return x so that x === x and I take the case branch."

http://jsfiddle.net/rufwork/upGH6/1/

/*global document*/
/*jslint evil:true*/
var x = 10;

switch (x) {
    case (x > 0 ? x : null):
        document.write('ha ha ha!  I fooled switch AND jsLint!  Muhahahahaha!');
        break;
    case 0:
        document.write('zero is nothing.');
        break;
    case -1:
        document.write('low');
        break;
    case -2:
        document.write('lower');
        break;
    case -3: 
        document.write('lowest I care about');
        break;
    default: // anything lower than -3.
        document.write('TOO LOW!!!! (unless you cheated and didn\'t use an int)');
}
document.write('<br>done.');

Quick reply to @Sv443:

Do notice that the default: switch says, "unless you cheated and didn't use an int" and that short circuiting requires x === x when you return x.

But your point is a useful reminder that NaN is the only case where short circuiting can't apply.

That is, x must == x to short circuit in switch and, as MDN tells us, "NaN, and only NaN, will compare unequal to itself" (double or triple =).

That also means that switching on a NaN value (and only a NaN value) will always hit default in ANY switch because you can't match its value.

Here's the full quote from MDN:

> NaN compares unequal (via ==, !=, ===, and !==) to any other value -- including to another NaN value. Use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN" >Number.isNaN() or https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN" >isNaN() to most clearly determine whether a value is NaN. Or perform a self-comparison: NaN, and only NaN, will compare unequal to itself.

You could change the default logic to check what you have:

isNaN(x) ? document.write ('nan') : document.write('TOO LOW!!!! ...)');

Or you even could go full hipster like MDN suggests (but please don't ;^D):

x !== x ? document.write ('nan') : document.write('TOO LOW!!!! ...)');

Solution 3 - Javascript

That is not how a switch block works. The case is used to hold a single value that if they are equal to the value on the switch line. if-else statements will serve you well.

Here is some information about the switch block.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch

Solution 4 - Javascript

Well, you can have expressions in case statement which is why your switch is not a syntax error. But you have to understand that the Case Clause is compared using === (strict comparison). Once you understand this, that the value must match exactly with the expression value in your switch(expression) you can shop around for expressions in js.

Function calls are expressions, so let's try with them:

function xbox(amount) { return amount >= 10000 && amount < 15000 && amount; }

function reward(amount) {
  var ps3 = function(amount) { return amount >= 7500 && amount < 10000 && amount; }

  function imac(amount) { return amount >= 15000 && amount; }

  var $reward = $("#reward");
  switch (amount) {
    case ps3(amount):
      $reward.text("Play Station 3");
      break;
    case xbox(amount):
      $reward.text("XBOX 360");
      break;
    case imac(amount):
      $reward.text("iMac");
      break;
    default:
      $reward.text("No reward");
      break;
  }
}
reward(8200)// -> Play Station 3
reward(11000)// -> XBOX 360
reward(20000)// -> iMac

As you can see, you can both use function expressions and function definitions. It doesn't matter. Only that the expression in the case clause is an expression to evaluate. Which is the same as you did, only you did not return a value that was the same as the amount but rather a true or false value. In my example I return the exact amount if my condition is true, hence trigger the comparison to match.

Here is your fixed code:

function reward(amount) {
    var $reward = $("#reward");
    switch (amount) {
        case (amount >= 7500 && amount < 10000 && amount):
            $reward.text("Play Station 3");
            break;
        case (amount >= 10000 && amount < 15000 && amount):
            $reward.text("XBOX 360");
            break;
        case (amount >= 15000 && amount):
            $reward.text("iMac");
            break;
        default:
            $reward.text("No reward");
            break;
    }
}

Here is the specification: https://tc39.github.io/ecma262/#sec-switch-statement The link is to es2016 because it's easier to lookup than the old es3 pdf from 1999. But it has always worked like this, but it is a little known fact.

I however doubt that this is faster than if statements. If you want your run to run fast, then do not touch the DOM.

Solution 5 - Javascript

You could also try one of my favorite constructions:

function reward(amount) {
    var $reward = $("#reward");
    $reward.text(
        (amount >= 7500 && amount < 10000) ?    "Play Station 3" :
        (amount >= 10000 && amount < 15000)?    "XBOX 360" :
        (amount >= 15000) ?                     "iMac" :
                                                "No reward"
    );
}

Solution 6 - Javascript

Firstly, that's not how switch works. You have to specify constants for each case, and those constants will be compared to the expression in parentheses (in your case, amount). That is how switch works, period.

Secondly, switch is not faster than several ifs

And thirdly, you shouldn't really worry about miniscule performance optimizations when you're dealing with javascript.

Solution 7 - Javascript

The problem is the switch expression cannot ever equal the case expressions, because the case expression will evaluate to true or false, but the switch expression will be a number.

The solution where the switch expression is set to true works not because true is a constant but because equality with the case expressions is actually possible.

It's not true that you have to specify constants for each case expression.

To back up my answer, refer to Douglas Crockford, Javascript The Good Parts (2008), page 12:

> The switch statement performs a multiway branch. It compares the expression for equality with all the selected cases.... When an exact match is found, the statements of the matching case clause are executed... A case clause contains one or more case expressions. The case expressions need not be constants.

Solution 8 - Javascript

My 2 cents:

Ideally switch (as a principle) should evaluate to a single case branch , thereby achieving O(1) performance and (other than fall through cases) the case statements can be reordered in any way without changing the compiler branching strategy.

If expressions are used (assuming the language allows it), then theoretically, it can follow more than branch.

The compiler (other than those which can intelligently say what the developer is trying to do) will not be able to optimize the branching strategy statically and ideally thus losing its effectiveness.

Example:

var x = 6, factors = [];

switch(x){

    case (x%2 == 0): factors.push(2);
    break;

    case (x%3 == 0): factors.push(3);
    break;
    ....
 }

{Expect comments on poor code}

In the above example, there is no practical way for the compiler to statically optimize, thus gains no performance benefit over if else.

The only part is that, it "may" look cleaner to the developer but could in-effect be cause for disruption in case of additional conditions.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMarkoView Question on Stackoverflow
Solution 1 - JavascriptMooGooView Answer on Stackoverflow
Solution 2 - JavascriptruffinView Answer on Stackoverflow
Solution 3 - JavascriptDaniel A. WhiteView Answer on Stackoverflow
Solution 4 - JavascriptdotnetCarpenterView Answer on Stackoverflow
Solution 5 - JavascriptrobertView Answer on Stackoverflow
Solution 6 - JavascriptFyodor SoikinView Answer on Stackoverflow
Solution 7 - JavascriptJon LylesView Answer on Stackoverflow
Solution 8 - JavascriptGNMView Answer on Stackoverflow