Expression inside switch case statement

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


You could always do

switch (true) {
  case (amount >= 7500 && amount < 10000):
    //code
    break;
  case (amount >= 10000 && amount < 15000):
    //code
    break;

  //etc...

It works because true is a constant, so the code under the first case statement 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 if/else statement would probably be more concise, and you'd not have to worry about accidental fall-through. But there it is anyway.


@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 Number.isNaN() or 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!!!! ...)');


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


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.