web-dev-qa-db-ja.com

大なり/小なりの切り替えステートメント

だから私はこのようなスイッチステートメントを使用したいです。

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

今、私はそれらのステートメント(<1000)または(>1000 && <2000)のどちらもうまくいかないことを知っています(明らかに異なる理由で)。私が求めているのは、それを実行するための最も効率的な方法です。私は30個のifステートメントを使用するのが嫌なので、スイッチ構文を使用したいと思います。私にできることはありますか?

194
switz

他の答えの解決策を見たとき、私はパフォーマンスにとって悪いことがわかっていることをいくつか見ました。私はそれらをコメントにするつもりでしたが、それをベンチマークして結果を共有する方が良いと思いました。あなたはそれを自分でテストすることができます。以下は、各ブラウザで最も速い操作の後に正規化された私の結果(ymmv)です(1.0時間に正規化された値を掛けて絶対時間(ミリ秒)を取得します)。

 Chrome Firefox Opera MSIEサファリノード
 ---------------------------------- --------------------------------- 
 1.0時間37ミリ秒73ミリ秒68ミリ秒184ミリ秒73ミリ秒21ミリ秒
 if-immediate 1.0 1.0 1.0 2.6 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 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 
スイッチ間接配列35.2 9.6 4.2 5.5 10.7 8.6 
配列線形スイッチ3.6 4.1 4.5 10.0 4.7 2.7 [。] [配列バイナリスイッチ] 7.8 6.7 9.5 16.0 15.0 4.9 

次のバージョンのWindows 7 32bitで実行した場合のテスト:Chrome 21.0.1180.89mFirefox 15.0Opera 12.02MSIE 9.0.8112Safari 5.1.7NodeはLinux 64ビットボックスで実行されました。Windows用のNode.jsのタイマーの解像度が1msではなく10msだったためです。

即時の場合

...drumrollMSIEを除くすべてのテスト済み環境で最速です。 (驚き、驚き)これはそれを実装するための推奨される方法です。

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

間接の場合

これはswitch-indirect-arrayの変種ですが、代わりにif-ステートメントを使用し、ほとんどすべてのテスト済み環境でswitch-indirect-arrayよりもはるかに高速に実行されます。

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

即時切替

これはテストされたすべての環境でかなり速く、実際にはMSIEの中で最も速いです。あなたがインデックスを得るためにあなたが計算をすることができるとき、それは働きます。

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

スイッチ範囲

これは、約1.5倍の時間がかかるOperaを除いて、テストされたすべての環境での最速よりも約6〜40倍遅くなります。エンジンはそれぞれの場合に対して2回値を比較しなければならないのでそれは遅いです。驚くべきことに、Chromeの最速の操作と比較して、Chromeがこれを完了するのに約40倍長い時間がかかりますが、MSIEは6倍の時間しかかかりません。しかし、実際の時差は1337msでMSIEに有利なわずか74msでした(!)。

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つしかないため高速ですが、Operaを除いて非常に遅いです。エンジンは各ケースをソースコード順にテストするため、caseステートメントの順序は重要です。 ECMAScript262:5 12.11

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

スイッチ間接配列

この変形では、範囲は配列に格納されます。これはテストしたすべての環境では遅く、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;
}

配列線形検索

これは、配列内の値の線形検索と、固定値を持つswitchステートメントの組み合わせです。これを使いたい理由は、値が実行時までわからないときです。テストされたすべての環境では遅く、MSIEではほぼ10倍の時間がかかります。

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の変種ですが、バイナリ検索があります。残念ながらそれは線形検索より遅いです。それが私の実装であるのか、それとも線形検索がより最適化されているのか私は知りません。キースペースが小さすぎることも考えられます。

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を使用してください。

624
some

代替案

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

デモ: http://jsfiddle.net/UWYzr/

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

通常の手順がある場合にのみ機能します。

編集:この解決策は支持を得続けているので、私はそれをアドバイスする必要があります mofoloの解決策 方が良い方法です

20
IcanDivideBy0

条件とその条件に対応する関数を使用してカスタムオブジェクトを作成できます。

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

このような場合にしたいことのために関数を定義します(function1、function2などを定義します)

そしてルールを「評価」する

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

私は30のifステートメントを使うのが嫌いです

文が読みやすく維持しやすい場合は何度も。私は上記のことをお勧めします。あなたがたくさんの条件将来のたくさんの成長の可能性があるときだけです。

更新
@Bradがコメントで指摘したように、条件が相互に排他的である(一度にそのうちの1つだけが当てはまる)場合、上限をチェックすることで十分であるはずです。

if(scrollLeft < oneRule.upperLimit)

は、条件が昇順で定義されていることを示しています(最初に低いもの、次に0 to 1000、次に1000 to 2000など)。

6
Nivas

//do stuffで何をしていますか?

次のようなことができるかもしれません。

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

これでうまくいくかどうかはテストされていませんが、if statementsの変数を設定するためにいくつかの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;
}
3
Jason Gennaro

これは別の選択肢です:

     switch (true) {
         case (value > 100):
             //do stuff
             break;
         case (value <= 100)&&(value > 75):
             //do stuff
             break;
         case (value < 50):
            //do stuff
             break;
     }
1
Pablo Claus

私の場合(パーセンテージを色分けし、パフォーマンスに重大な影響を与えるものは何もありません)、私はすぐにこれを書きました:

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

    return colors.find((col, index) => {
        return index >= thresholds.length || progress < thresholds[index];
    });
}
1
grebenyuksv

受け入れられた回答を更新します(まだコメントできません)。クロムでデモjsfiddleを使用している1/12/16現在、switch-immediateが最速のソリューションです。

結果:時間分解能: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

終了しました

 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
1
jeffhale

私は30のifステートメントを使うのが嫌いです

私は最近同じ状況を経験しました、それが私がそれを解決した方法です:

前:

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

後:

var scales = [[4, 1], [6, 2], [9, 3], [12, 4], [18, 5]];
scales.forEach(function(el){if(wind_speed > el[0]) scale = el[1]});

そして、 "1、2、3、4、5"を設定すれば、さらに簡単になります。

var scales = [4, 6, 9, 12, 18];
scales.forEach(function(el){if(wind_speed >= el) scale++});
1
Martin