Coder Perfect

greater-than/less-than switch statement

Problem

As a result, I’d like to utilize a switch statement similar to this:

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

Now I know that neither (1000) nor (>1000 && 2000) will work (for different reasons, obviously). What I’m curious about is the most efficient approach to accomplish this. I’d rather use the switch syntax than use 30 if statements. Is there anything I can do to help?

Asked by switz

Solution #1

When I looked at the other respondents’ solutions, I noticed some things that I know are terrible for performance. I was going to put these in a comment, but I decided to benchmark them and provide the findings instead. You can put it to the test yourself. The results (ymmv) are listed below, normalized after the fastest operation in each browser.

The results from 2021-MAY-05 are listed below.

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 were tested in 2021 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)

Safari for Windows is still at version 5.1.7 since Apple does not update it. In this test, I converted it to Brave.

For historical comparison, here are the results from 2012-September-04:

Chrome 21.0.1180.89m, Firefox 15.0, Opera 12.02, MSIE 9.0.8112, Safari 5.1.7 were tested in 2012 on Windows 7 32bit with the following versions: Chrome 21.0.1180.89m, Firefox 15.0, Opera 12.02, MSIE 9.0.8112, Safari 5.1.7. Because the timing resolution on Node for Windows was 10ms instead of 1ms, it was ran on a Linux 64bit system.

Except for… drumroll… MSIE, this is the fastest way in all tested situations. (It’s a surprise, it’s a surprise)

This is the preferred method of implementation.

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

This is a switch-indirect-array variation that uses if-statements instead of switch-indirect-array and is faster in all tested engines.

In 2021, it was 20-120 percent slower than the fastest test (2012: 0-280 percent). Chrome will take longer in 2021 (2.20) than it did 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

When you can execute a calculation to get an index, this works.

Except in MSIE, where it was really the fastest, it was 40-120 percent (2012: 0-180 percent) slower than if-immediate in 2021.

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

It takes a long time because the engine has to compare the value twice for each causation.

It was 1-2.6 times slower than the fastest test in 2021 (2012: 1.6-38). Chrome has improved the most, from 38 to 3.6, although it is still the slowest engine tested.

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 switch-range variation with only one compare per case, making it faster. Because the engine will test each case in source code order, the order in which the case statements are written is critical. ECMAScript 2020 (version 13.12.9)

It was 36-107 percent slower in 2021 than the fastest test, but 1-31 times slower in 2012. Chrome continues to perform poorly on this test, however 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;
}

The ranges are stored as an array in this variant.

In 2021, it was 57-193 percent slower than the fastest test (2012: 3-35 times). All of the evaluated engines have improved their performance, and while Chrome remains 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;
}

The ranges are stored as an array in this variant.

In 2021, it was 57-193 percent slower than the fastest test (2012: 3-35 times). All of the evaluated engines have improved their performance, and while Chrome remains 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;
}

This is a binary search variation of array-linear-switch. Unfortunately, it is more time consuming than a linear search. I’m not sure if it’s because of my implementation or because the linear search is more efficient. It’s also possible that the keyspace is insufficient.

This was 4-5 (2012: 4-16) times slower in 2021. Don’t use it.

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 speed is critical, use if-statements or switch statements with immediate values.

Answered by some

Solution #2

An alternative:

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

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

Answered by labue

Solution #3

switch (Math.floor(scrollLeft/1000)) {
  case 0: // (<1000)
   //do stuff
   break;
  case 1: // (>=1000 && <2000)
   //do stuff;
   break;
}

Only if you take regular steps will it work…

EDIT: Given the high number of upvotes for this approach, I feel compelled to point out that mofolo’s method is far superior.

Answered by IcanDivideBy0

Solution #4

You can make a custom object that contains the criteria and the method that corresponds 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 to perform what you want in these situations (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

If statements are frequently easier to read and maintain. Only if you have a lot of circumstances and the potential for a lot of future growth would I recommend the above.

Update If the conditions are mutually exclusive (only one of them can be true at the same time), verifying the upper limit should suffice, as @Brad pointed out in the comments:

if(scrollLeft < oneRule.upperLimit)

assuming the prerequisites are listed in ascending order (first the lowest one, 0 to 1000, and then 1000 to 2000 for example)

Answered by Nivas

Solution #5

What are you doing in /do something, exactly?

You might be able to perform anything along the lines of:

(scrollLeft < 1000) ? //do stuff
: (scrollLeft > 1000 && scrollLeft < 2000) ? //do stuff
: (scrollLeft > 2000) ? //do stuff
: //etc. 

Answered by Igor

Post is based on https://stackoverflow.com/questions/6665997/switch-statement-for-greater-than-less-than