Switch statement for greater-than/less-than Ask Question

Switch statement for greater-than/less-than Ask Question

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?

ベストアンサー1

Looking at the solutions in the other answers, I saw a few things that I know from past experience are bad for performance.

I've tested several solutions, in different browsers, and below are my results (ymmv) normalized by 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.

2021 年には、最速のテストよりも 1 ~ 2.6 倍 (2012 年: 1.6 ~ 38 倍) 遅くなりました。Chrome は 38 倍から 3.6 倍に最も大きな改善を遂げましたが、依然としてテストされたエンジンの中で最も遅いエンジンです。

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;
}

スイッチ範囲2

これはのバリエーションですswitch-rangeが、ケースごとに比較が1つだけなので高速です。エンジンはソースコードの順序で各ケースをテストするため、ケースステートメントの順序は重要です。ECMAScript 2020 13.12.9

2021年は最速テストより36~107%遅くなりましたが、2012年には1~31倍遅くなりました。このテストで最悪のパフォーマンスを出しているのは依然としてChromeですが、32倍から2倍に改善されました。

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

スイッチ間接配列

このバリアントでは、範囲は配列に格納されます。

2021 年には、最速のテストよりも 57 ~ 193% (2012 年: 3 ~ 35 倍) 遅くなりました。テストされたすべてのエンジンでパフォーマンスが向上しており、Chrome は依然として最も遅いものの、35 倍から 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;
}

配列線形探索

このバリアントでは、範囲は配列に格納されます。

2021 年には、最速のテストよりも 57 ~ 193% (2012 年: 3 ~ 35 倍) 遅くなりました。テストされたすべてのエンジンでパフォーマンスが向上しており、Chrome は依然として最も遅いものの、35 倍から 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-linear-switchが、バイナリ検索を使用します。残念ながら、線形検索よりも遅くなります。これが私の実装によるものか、線形検索の方が最適化されているのかはわかりません。キースペースが小さすぎる可能性もあります。

2021 年には 4 ~ 5 倍 (2012 年: 4 ~ 16 倍) 遅くなりました。を使用しないでください

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- ステートメントまたは を使用します。switch

おすすめ記事