so I want to use a switch statement like this:

switch (scrollLeft) {
  case (<1000):
   //do stuff
   break;
  case (>1000 && <2000):
   //do stuff
   break;
}

Now I know that either of those statements (<1000) or (>1000 && <2000) won't work (for different reasons, obviously). What I'm asking is the most efficient way to do just that. I hate using 30 if statements, so I'd rather use the switch syntax. Is there anything that I can do?

share|improve this question
5  
does your steps are regular ? I mean, if you divide scrollLeft by 1000, you can switch 1, 2, 3... – IcanDivideBy0 Jul 12 '11 at 14:42
    
Maybe you could make a sorted array that maps a condition range with corresponding operation, and apply a binary search on it. Or if your conditions are regular enough, you could directly call your_mapper_object[scrollLeft / SOME_CONST], assuming your_mapper_object is something like {1: some_func, 2: another_func, ...}. And in this case you could also use switch. – Overmind Jiang Jul 12 '11 at 14:45
    
@Switz, why not if-else? – Phillip Kovalev Jul 12 '11 at 14:46
up vote 366 down vote accepted

When I looked at the solutions in the other answers I saw some tings that I know where bad for performance. I was going to put it in a comment but I thought it was better to benchmark it and share the results. You can test it yourself. Below is my results (ymmv) normalized after the fastest operation in each browser (multiply the 1.0 time with the normalized value to get the absolute time in ms).

                    Chrome  Firefox Opera   MSIE    Safari  Node
-------------------------------------------------------------------
1.0 time               37ms    73ms    68ms   184ms    73ms    21ms
if-immediate            1.0     1.0     1.0     2.6     1.0     1.0
if-indirect             1.2     1.8     3.3     3.8     2.6     1.0
switch-immediate        2.0     1.1     2.0     1.0     2.8     1.3
switch-range           38.1    10.6     2.6     7.3    20.9    10.4
switch-range2          31.9     8.3     2.0     4.5     9.5     6.9
switch-indirect-array  35.2     9.6     4.2     5.5    10.7     8.6
array-linear-switch     3.6     4.1     4.5    10.0     4.7     2.7
array-binary-switch     7.8     6.7     9.5    16.0    15.0     4.9

Test where performed on Windows 7 32bit with the folowing versions: Chrome 21.0.1180.89m, Firefox 15.0, Opera 12.02, MSIE 9.0.8112, Safari 5.1.7. Node was run on a Linux 64bit box because the timer resolution on Node.js for Windows was 10ms instead of 1ms.

if-immediate

This is the fastest in all tested environments, except in ... drumroll MSIE! (surprise, surprise). This is the recommended way to implement it.

if (val < 1000) { /*do something */ } else
if (val < 2000) { /*do something */ } else
...
if (val < 30000) { /*do something */ } else

if-indirect

This is a variant of switch-indirect-array but with if-statements instead and performs much faster than switch-indirect-array in almost all tested environments.

values=[
   1000,  2000, ... 30000
];
if (val < values[0]) { /* do something */ } else
if (val < values[1]) { /* do something */ } else
...
if (val < values[29]) { /* do something */ } else

switch-immediate

This is pretty fast in all tested environments, and actually the fastest in MSIE. It works when you can do a calculation to get an index.

switch (Math.floor(val/1000)) {
  case 0: /* do something */ break;
  case 1: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

switch-range

This is about 6 to 40 times slower than the fastest in all tested environments except for Opera where it takes about one and a half times as long. It is slow because the engine has to compare the value twice for each case. Surprisingly it takes Chrome almost 40 times longer to complete this compared to the fastest operation in Chrome, while MSIE only takes 6 times as long. But the actual time difference was only 74ms in favor to MSIE at 1337ms(!).

switch (true) {
  case (0 <= val &&  val < 1000): /* do something */ break;
  case (1000 <= val &&  val < 2000): /* do something */ break;
  ...
  case (29000 <= val &&  val < 30000): /* do something */ break;
}

switch-range2

This is a variant of switch-range but with only one compare per case and therefore faster, but still very slow except in Opera. The order of the case statement is important since the engine will test each case in source code order ECMAScript262:5 12.11

switch (true) {
  case (val < 1000): /* do something */ break;
  case (val < 2000): /* do something */ break;
  ...
  case (val < 30000): /* do something */ break;
}

switch-indirect-array

In this variant the ranges is stored in an array. This is slow in all tested environments and very slow in Chrome.

values=[1000,  2000 ... 29000, 30000];

switch(true) {
  case (val < values[0]): /* do something */ break;
  case (val < values[1]): /* do something */ break;
  ...
  case (val < values[29]): /* do something */ break;
}

array-linear-search

This is a combination of a linear search of values in an array, and the switch statement with fixed values. The reason one might want to use this is when the values isn't known until runtime. It is slow in every tested environment, and takes almost 10 times as long in MSIE.

values=[1000,  2000 ... 29000, 30000];

for (sidx=0, slen=values.length; sidx < slen; ++sidx) {
  if (val < values[sidx]) break;
}

switch (sidx) {
  case 0: /* do something */ break;
  case 1: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

array-binary-switch

This is a variant of array-linear-switch but with a binary search. Unfortunately it is slower than the linear search. I don't know if it is my implementation or if the linear search is more optimized. It could also be that the keyspace is to small.

values=[0, 1000,  2000 ... 29000, 30000];

while(range) {
  range = Math.floor( (smax - smin) / 2 );
  sidx = smin + range;
  if ( val < values[sidx] ) { smax = sidx; } else { smin = sidx; }
}

switch (sidx) {
  case 0: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

Conclusion

If performance is important, use if-statements or switch with immediate values.

share|improve this answer
56  
Its rare to see an answer with this much detail and tidy structure. Big +1 – Rick Donohoe Nov 29 '12 at 14:50
4  
Big +1 for the explanation of performance side of this problem! – Zoltán Schmidt Jul 23 '13 at 15:25
8  
This is the reason stackoverflow is one of the best places for answers. This is a "timeless" answer, great job and thanks for the jsfiddle! – Jessycormier Jan 22 '14 at 13:51
1  
grt info & explainination – JayKandari Feb 5 '16 at 7:52
1  
I really wish I could +2, such a detailed answer! – Druzion Mar 17 '16 at 16:59

An alternative:

var scrollleft = 1000;
switch (true)
{
    case (scrollleft > 1000):
      alert('gt');
      break;
   case (scrollleft <= 1000):
      alert('lt');
      break; 
}

Demo: http://jsfiddle.net/UWYzr/

share|improve this answer
3  
this is a more valueable solution. +1 – IcanDivideBy0 Jul 12 '11 at 16:17
    
Nice solution to work with both regular and irregular steps. +1 – Witman Feb 28 '12 at 3:58
    
+1 I just used this (a year later); thanks! – msanford Apr 5 '12 at 14:22
1  
Isn't this just the same as if(...) else if(...)? This does avoid if but doesn't quite sound like a pretty replacement to me. – pimvdb Jul 19 '12 at 14:03
4  
While elegant to code, it hurts performance. It is almost 30 times slower in Chrome than using if-statements. See my answer here – some Sep 4 '12 at 8:49
switch (scrollLeft/1000) {
  case 0: // (<1000)
   //do stuff
   break;
  case 1: // (>=1000 && <2000)
   //do stuff;
   break;
}

Only works if you have regular steps...

EDIT: since this solution keeps getting upvotes, I must advice that mofolo's solution is a way better

share|improve this answer
1  
I used Math.round(scrollLeft/1000) by the way. – switz Jul 12 '11 at 15:03
    
@Switz - Just keep in mind that 999 < 1000 falls into case 0 but Math.round(999/1000) falls into case 1. Also, there is a typo above, in that case 1 is >= 1000, not just >1000. – Igor Jul 12 '11 at 18:27
1  
@IcanDivideBy0 +1 for pointing to mofolo's solution – krul Apr 18 '12 at 19:45
    
Only problem with mofolo's solution is that it is about 30 times slower in Chrome than the one by IcanDivideBy0. Se my answer below. – some Sep 4 '12 at 9:01

You can create a custom object with the criteria and the function corresponding to the criteria

var rules = [{ lowerLimit: 0,    upperLimit: 1000, action: function1 }, 
             { lowerLimit: 1000, upperLimit: 2000, action: function2 }, 
             { lowerLimit: 2000, upperLimit: 3000, action: function3 }];

Define functions for what you want to do in these cases (define function1, function2 etc)

And "evaluate" the rules

function applyRules(scrollLeft)
{
   for(var i=0; i>rules.length; i++)
   {
       var oneRule = rules[i];
       if(scrollLeft > oneRule.lowerLimit && scrollLeft < oneRule.upperLimit)
       {
          oneRule.action();
       }
   }
}

Note

I hate using 30 if statements

Many times if statements are easier to read and maintain. I would recommend the above only when you have a lot of conditions and a possibility of lot of growth in the future.

Update
As @Brad pointed out in the comments, if the conditions are mutually exclusive (only one of them can be true at a time), checking the upper limit should be sufficient:

if(scrollLeft < oneRule.upperLimit)

provided that the conditions are defined in ascending order (first the lowest one, 0 to 1000, and then 1000 to 2000 for example)

share|improve this answer
    
action=function1 -- shouldn't these be colons? ;-) -- You can also refactor this to only have an upper limit as, due to process of elimination, you can't fall within two groups--unless that was your intent (to have multiple actions possible). – Brad Christie Jul 12 '11 at 14:49
    
@Brad Christie Of Course – Nivas Jul 12 '11 at 14:49
    
@Brad, no that was not my intention, and you are right, the upper limit should suffice. Will add that as an update... – Nivas Jul 13 '11 at 3:21
    
I find this one concise and clean +1 – pimvdb Jul 13 '11 at 7:33

Untested and unsure if this will work, but why not do a few if statements before, to set variables for the switch statement.

var small, big;

if(scrollLeft < 1000){
    //add some token to the page
    //call it small
}


switch (//reference token/) {
  case (small):
   //do stuff
   break;
  case (big):
   //do stuff;
   break;
}
share|improve this answer

What exactly are you doing in //do stuff?

You may be able to do something like:

(scrollLeft < 1000) ? //do stuff
: (scrollLeft > 1000 && scrollLeft < 2000) ? //do stuff
: (scrollLeft > 2000) ? //do stuff
: //etc. 
share|improve this answer

This is another option:

     switch (true) {
         case (value > 100):
             //do stuff
             break;
         case (value <= 100)&&(value > 75):
             //do stuff
             break;
         case (value < 50):
            //do stuff
             break;
     }
share|improve this answer

Updating the accepted answer (can't comment yet). As of 1/12/16 using the demo jsfiddle in chrome, switch-immediate is the fastest solution.

Results: Time resolution: 1.33

   25ms "if-immediate" 150878146 
   29ms "if-indirect" 150878146
   24ms "switch-immediate" 150878146
   128ms "switch-range" 150878146
   45ms "switch-range2" 150878146
   47ms "switch-indirect-array" 150878146
   43ms "array-linear-switch" 150878146
   72ms "array-binary-switch" 150878146

Finished

 1.04 (   25ms) if-immediate
 1.21 (   29ms) if-indirect
 1.00 (   24ms) switch-immediate
 5.33 (  128ms) switch-range
 1.88 (   45ms) switch-range2
 1.96 (   47ms) switch-indirect-array
 1.79 (   43ms) array-linear-switch
 3.00 (   72ms) array-binary-switch
share|improve this answer
    
it really depends - 15ms "if-immediate" 15ms "if-indirect" 15ms "switch-immediate" 37ms "switch-range" 28ms "switch-range2" 35ms "switch-indirect-array" 29ms "array-linear-switch" 62ms "array-binary-switch" Finished 1.00 ( 15ms) if-immediate 1.00 ( 15ms) if-indirect 1.00 ( 15ms) switch-immediate 2.47 ( 37ms) switch-range 1.87 ( 28ms) switch-range2 2.33 ( 35ms) switch-indirect-array 1.93 ( 29ms) array-linear-switch 4.13 ( 62ms) array-binary-switch chrome Version 48.0.2564.109 (64-bit) mac os x 10.11.3 – RenaissanceProgrammer Feb 19 '16 at 2:20
    
ATM Safari 9.X on Mac OS x and Safari ios 9.3,"if-immediate" is the clear winner – RenaissanceProgrammer Feb 19 '16 at 2:27

In my case (color-coding a percentage, nothing performance-critical), I quickly wrote this:

function findColor(progress) {
    const thresholds = [30, 60];
    const colors = ["#90B451", "#F9A92F", "#90B451"];

    return colors.find((col, index) => {
        return index >= thresholds.length || progress < thresholds[index];
    });
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.