Switch statement for greater-than/less-than
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?
When I looked at the solutions in the other answers I saw some things that I know are bad for performance. I was going to put them in a comment but I thought it was better to benchmark it and share the results. You can test it yourself. Below are my results (ymmv) normalized after the fastest operation in each browser.
Here is the results from 2021-MAY-05
Test | Chrome | Firefox | Opera | Edge | Brave | Node |
---|---|---|---|---|---|---|
1.0 time | 15 ms | 14 ms | 17 ms | 17 ms | 16 ms | 14 ms |
if-immediate | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 |
if-indirect | 2.20 | 1.21 | 2.06 | 2.18 | 2.19 | 1.93 |
switch-immediate | 2.07 | 1.43 | 1.71 | 1.71 | 2.19 | 1.93 |
switch-range | 3.60 | 2.00 | 2.47 | 2.65 | 2.88 | 2.86 |
switch-range2 | 2.07 | 1.36 | 1.82 | 1.71 | 1.94 | 1.79 |
switch-indirect-array | 2.93 | 1.57 | 2.53 | 2.47 | 2.75 | 2.50 |
array-linear-switch | 2.73 | 3.29 | 2.12 | 2.12 | 2.38 | 2.50 |
array-binary-switch | 5.80 | 6.07 | 5.24 | 5.24 | 5.44 | 5.37 |
The tests in 2021 where performed on Windows 10 64bit with the following versions: Chrome 90.0.4430.212, Firefox 89.0b13, Opera 76.0.4017.123, Edge 90.0.818.62, Brave 1.24.85, and Node 16.1.0 (was run under WSL)
Apple doesn't update Safari for Windows, so it is still 5.1.7. I changed it to Brave in this test.
Here is the results from 2012-September-04, for historical comparison:
Test | Chrome | Firefox | Opera | MSIE | Safari | Node |
---|---|---|---|---|---|---|
1.0 time | 37 ms | 73 ms | 68 ms | 184 ms | 73 ms | 21 ms |
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 |
The tests in 2012 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 for Windows was 10ms instead of 1ms.
if-immediate
This is the fastest method 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 is faster in all tested engines.
In 2021 it was 20-120% (2012: 0-280%) slower than the fastest test. Chrome takes longer time in 2021 (2.20) than in 2012 (1.2)
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 works when you can do a calculation to get an index.
In 2021 it was 40-120% (2012: 0-180%) slower than if-immediate
, except in MSIE where it actually was the fastest.
switch (Math.floor(val/1000)) {
case 0: /* do something */ break;
case 1: /* do something */ break;
...
case 29: /* do something */ break;
}
switch-range
It is slow because the engine has to compare the value twice for each case.
In 2021 it was 1-2.6 (2012: 1.6-38) times slower than the fastest test. Chrome has made the biggest improvement from 38 to 3.6, but is still the slowest tested engine.
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.
The order of the case statement is important since the engine will test each case in source code order ECMAScript 2020 13.12.9
In 2021 it was 36-107% slower than the fastest test, but in 2012 it was 1-31 times slower. It is still Chrome who has the worst performance on this test, but it has improved from 32 to 2 times.
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.
In 2021 it was 57-193% (2012: 3-35 times) slower than the fastest test. The perormance has improved in all tested engines, and while Chrome is still the slowest, it has improved from 35 to 2.93.
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
In this variant the ranges is stored in an array.
In 2021 it was 57-193% (2012: 3-35 times) slower than the fastest test. The perormance has improved in all tested engines, and while Chrome is still the slowest, it has improved from 35 to 2.93.
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.
In 2021 this was 4-5 (2012: 4-16) times slower. Do not use.
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.
An alternative:
var scrollleft = 1000;
switch (true)
{
case (scrollleft > 1000):
alert('gt');
break;
case (scrollleft <= 1000):
alert('lt');
break;
}
Demo: http://jsfiddle.net/UWYzr/