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?
This question is related to
javascript
jquery
comparison
switch-statement
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 (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.
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
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
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
If performance is important, use if
-statements or switch
with immediate values.
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
An alternative:
var scrollleft = 1000;
switch (true)
{
case (scrollleft > 1000):
alert('gt');
break;
case (scrollleft <= 1000):
alert('lt');
break;
}
switch (Math.floor(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
I hate using 30 if statements
I had the same situation lately, that's how I solved it:
before:
if(wind_speed >= 18) {
scale = 5;
} else if(wind_speed >= 12) {
scale = 4;
} else if(wind_speed >= 9) {
scale = 3;
} else if(wind_speed >= 6) {
scale = 2;
} else if(wind_speed >= 4) {
scale = 1;
}
after:
var scales = [[4, 1], [6, 2], [9, 3], [12, 4], [18, 5]];
scales.forEach(function(el){if(wind_speed > el[0]) scale = el[1]});
And if you set "1, 2, 3, 4, 5" then it can be even simpler:
var scales = [4, 6, 9, 12, 18];
scales.forEach(function(el){if(wind_speed >= el) scale++});
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;
}
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)
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;
}
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.
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];
});
}
Source: Stackoverflow.com