12

Since NaN === NaN evaluates to false, is it possible to add a NaN case to a switch statement?

For example, let's say I want to make the following switch:

switch(x){
    case 1:
    case 2:
    case 4:
        doSomething();
        break;
    case NaN:
        doSomethingElse();
        break;
    case default:
        doADifferentThing();
        break;
}

Sending NaN as x will go to the default case. I know there are ways around using NaN in switch statements (e.g. I can surround with an if..else statement and use isNaN), but is there a more direct approach?

Danilo Valente
  • 11,270
  • 8
  • 53
  • 67
Michael Holman
  • 901
  • 1
  • 10
  • 26
  • 3
    I believe the only way to do this is `switch(true)` with `case x===1` and `case isNaN(x)` statements. Not the most ideal structure, but does work. – Brad Jul 25 '12 at 20:55
  • 3
    You could just do isNan in the default case, and change your logic accordingly – nolegs Jul 25 '12 at 20:57
  • 1
    Put `case` blocks for every single other possible value of `x`, and then `default` will be `NaN`. :) – tenfour Jul 25 '12 at 21:00

4 Answers4

16

I originally wrote i saw only one solution, however during my sleep i came up with a superb solution.

Always keep in mind that a switch does not do implicit type conversion to compare the cases so if you provide a string to the switch statement it will not match to integers in the cases, and vice versa. If you want to compare to strings and integers you will have to cast your integer to a string first and then compare to strings only.

The superb solution:

As pointed out by WouterH, my initial solution will resolve to default when using a string that contains a number, this is expected behavior for switch statements. But it might be useful to parse the argument in order to overcome this. For that you can use following code:

var x = "1";
switch (isNaN(x) || parseInt(x))
{
    case true:
      alert("IsNaN!") 
      break; 
    case 1: 
      alert("1"); 
      break; 
    case 2: 
      alert("2"); 
      break; 
    case 4: 
      alert("4"); 
      break; 
    default: 
      alert("default"); 
      break; 
}

My initial superb method :

var x = "clearly not a number";
switch(x){
    case !isNaN(x) || x:
      alert("IsNaN!")
      break;
    case 1:
      alert("1");
      break;
    case 2:
      alert("2");
      break;
    case 4:
      alert("4");
      break;
    default:
      alert("default");
      break;
    }

isNaN will return true if x where to be a string but it doesn't really matter because true won't evaluate as true to a string because of the above mentioned behavior of the switch statement.

My original solution:

I don't even know what i was thinking, this looks horrible and the indentation is just plain awkward, but thanks for the upvotes !

var x = "clearly not a number";
switch(x){
    case 1:
      alert("1");
      break;
    case 2:
      alert("2");
      break;
    case 4:
      alert("4");
      break;
    case default:
       if (isNaN(x)){
          alert("isNaN");
          break;
       }
       alert("default");
       break;
}

Brad's solution:

thx to Brad for this one. I don't really like this because it feels a bit like a hack, that is to say, this isn't how you would expect usage of a case statement, but it does give you the most flexibility, so i'm certain there is a use case for it.

var x = "clearly not a number";
switch(true)
{
   case x==1:
      alert("1");
      break;
   case x==2:
      alert("2");
      break;
   case IsNaN(x):
      alert("IsNaN");
      break;
   case default:
      alert("default");
      break;
}
Willem D'Haeseleer
  • 19,661
  • 9
  • 66
  • 99
  • 1
    sorry, but the superb solution fails when you pass a number as a string: `var x = "1";` see this [jsFiddle](http://jsfiddle.net/MAAV5/) – huysentruitw Aug 09 '12 at 20:10
  • 1
    @WouterH, this is the expected behavior of a switch statement, it does not do implicit conversions, like i pointed out explicitly in my post, please see this jsFiddle for proof http://jsfiddle.net/DYcfP/1/ – Willem D'Haeseleer Aug 09 '12 at 20:34
  • For `x = "1"` I would expect it alerts `1` or `IsNaN!`, but not `default`. – huysentruitw Aug 09 '12 at 20:37
  • @WouterH this is how javascript switch statements work so it IS as expected, but your parsing method does provide an extra feature that might be useful – Willem D'Haeseleer Aug 09 '12 at 21:01
  • oh, man, it looks awesome. but in the same time I'd never allow something alike passing code review :) – skyboyer Oct 04 '19 at 08:19
3

You could do this (jsFiddle):

var x = "test";
switch (isNaN(x) || x)
{
    case true:
      alert("IsNaN!") 
      break; 
    case 1: 
      alert("1"); 
      break; 
    case 2: 
      alert("2"); 
      break; 
    case 4: 
      alert("4"); 
      break; 
    default: 
      alert("default"); 
      break; 
}

Or if you also want to validate string containing a number (jsFiddle):

var x = "1";
switch (isNaN(x) || parseInt(x))
{
    case true:
      alert("IsNaN!") 
      break; 
    case 1: 
      alert("1"); 
      break; 
    case 2: 
      alert("2"); 
      break; 
    case 4: 
      alert("4"); 
      break; 
    default: 
      alert("default"); 
      break; 
}
huysentruitw
  • 27,376
  • 9
  • 90
  • 133
2

@helmus's answer is correct and is a good solution.

However, you can maintain the NaN case if you use strings:

switch(x+''){
    case "1":
    case "2":
    case "4":
        doSomething();
        break;
    case "NaN":
        doSomethingElse();
        break;
    default:
        doADifferentThing();
        break;
}
Danilo Valente
  • 11,270
  • 8
  • 53
  • 67
  • 1
    That'll work as long as you know `x` isn't `null` or `undefined`. You could do `x + ""` instead. –  Jul 25 '12 at 21:04
  • ...and technically speaking, `NaN !== "NaN"`. – josh3736 Jul 25 '12 at 21:05
  • Oops, just though of another possible breakage. If it's a number in an Array, like `[1]`, the string conversion will give you "1". I guess it depends on what sort of type coercion OP wants, if any. The `switch` statement is a strict comparison, so perhaps coerced values aren't desired. –  Jul 25 '12 at 21:06
  • if x contains the string "NaN" this will resolve to the wrong case – Willem D'Haeseleer Jul 25 '12 at 21:07
  • @josh3736 I know, that's why I've converted `x` and all the cases to strings. Then the comparison will be `"NaN"=="NaN"` – Danilo Valente Jul 25 '12 at 21:07
  • @amnotiam @helmus Yes, there are a lot of possible breakages in this code if the OP is using a var of type different of `number`. But I wrote this answer expecting that he will only work with numbers, NaN, null and undefined – Danilo Valente Jul 25 '12 at 21:11
  • What I mean is that a string containing "NaN" does not mean the same thing as the primitive value `NaN`. Coercing to string *potentially* results in incorrect comparison. – josh3736 Jul 25 '12 at 21:14
  • @josh3736 Yes, but as i said in the comment above, I'm expecting OP will only use numbers, NaN, null and undefined – Danilo Valente Jul 25 '12 at 21:20
0

use toString():

switch (x.toString()) {
    case '1':
    case '2':
    case '4':
        console.log('1/2/4');
        break;
    case 'NaN':
        console.log('NaN');
        break;
    default:
        console.log('default');
}