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


230

だから私はこのようなswitch文を使いたい:

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

これらのステートメント(<1000)または(>1000 && <2000)のいずれかが機能しないことがわかりました(さまざまな理由で)。私が求めているのは、それを行う最も効率的な方法です。私は30個のifステートメントを使用するのが嫌いなので、代わりにスイッチ構文を使用します。何かできることはありますか?


5
あなたのステップは定期的ですか?つまり、scrollLeftを1000で除算すると、1、2、3を切り替えることができます...
IcanDivideBy0

おそらく、条件範囲を対応する操作にマップするソートされた配列を作成し、それにバイナリ検索を適用することができます。またはあなたの条件が十分に規則的であるならば、あなたは直接呼び出すことができますyour_mapper_object[scrollLeft / SOME_CONST]、と仮定するyour_mapper_objectようなものです{1: some_func, 2: another_func, ...}。この場合、スイッチを使用することもできます。
Jiangを

回答:


731

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

                    Chrome Firefox Opera MSIE Safariノード
-------------------------------------------------- -----------------
1.0時間37ms 73ms 68ms 184ms 73ms 21ms
if-immediate 1.0 1.0 1.0 2.6 1.0 1.0
if間接1.2 1.8 3.3 3.8 2.6 1.0
スイッチ即時2.0 1.1 2.0 1.0 2.8 1.3
スイッチ範囲38.1 10.6 2.6 7.3 20.9 10.4
スイッチ範囲2 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 32ビットで実行した場所でテストします:Chrome 21.0.1180.89mFirefox 15.0Opera 12.02MSIE 9.0.8112Safari 5.1.7。Node.js for Windowsのタイマー解像度が1msではなく10msだったため、NodeはLinux 64ビットボックスで実行されました。

if-immediate

これは、... ドラムロール MSIE を除いて、すべてのテスト済み環境で最速です。(驚き、驚き)。これは、これを実装するための推奨される方法です。

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

間接的

これはのバリアントですswitch-indirect-arrayが、if代わりに-statementsがあり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回比較する必要があるため、処理が遅くなります。驚いたことに、MSIEは6倍の時間しか要しませんが、Chromeは最速の操作と比較して、これを完了するのに約40倍長くかかります。しかし、実際の時間差は74msで、MSIEが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;
}

スイッチ範囲2

これはの変形ですswitch-rangeが、ケースごとに比較が1つしかないため、高速ですが、Operaを除いて非常に低速です。エンジンは各ケースをソースコード順に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-statementsまたはswitch即値を使用してください。


128
この非常に詳細で整頓された構造で答えを見ることはまれです。ビッグ+1
リックドノホー

10
この問題のパフォーマンス面の説明のための大きな+1!
ゾルタンシュミット

16
これが、stackoverflowが回答の最良の場所の1つである理由です。これは「時代を超越した」答えで、すばらしい仕事であり、jsfiddleに感謝します。
Jessy

1
grt情報と説明
JayKandari、2016

3
+2、このような詳細な答えができたら本当に嬉しいです!
Kaspar Lee

96

代替:

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

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


4
これはより価値のあるソリューションです。+1
IcanDivideBy0

1
これはまったく同じではありませんif(...) else if(...)か?これは回避しますifが、私にとってかなりの置き換えのようには聞こえません。
pimvdb 2012

7
コードはエレガントですが、パフォーマンスが低下します。Chromeでは、if-statements を使用するよりも約30倍遅くなります。私を参照してくださいここに答えを
いくつかの

1
ただし、処理されるデータが大きくなく、単一のユーザー入力の検証など、その機能のみが適用されている場合は、このようなパフォーマンスのペナルティは無視できます。
ヘススフランコ

1
これはまさに私が探していたものです。ありがとう!
アミシュライバー

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

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

編集:このソリューションは賛成票を獲得し続けるので、mofoloのソリューションがより優れていることをアドバイスする必要があります


1
Math.round(scrollLeft/1000)ちなみに使用しました。
switz

@Switz-999 <1000はケース0に該当しますが、Math.round(999/1000)はケース1に該当します。また、上記のタイプミスがあります。この場合、1は>> 1000ではなく>> 1000です。 。
イゴール

mofoloのソリューションの唯一の問題は、IcanDivideBy0によるものよりもChromeの方が約30倍遅いということです。下記の私の回答をご覧ください。
いくつかの

6

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

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つしかtrueになり得ない場合)、上限を確認するだけで十分です。

if(scrollLeft < oneRule.upperLimit)

提供条件は、昇順に定義されていること(第一最低一つ0 to 1000、次いで1000 to 2000例えば)


action=function1-これらはコロンではありませんか?;-)-これをリファクタリングして上限のみを設定することもできます。これは、消去のプロセスにより、意図が(複数のアクションを可能にする)場合を除き、2つのグループに入ることはできないためです。
ブラッドクリスティ

@ブラッド・クリスティー・オブ・コース
ニバス

@ブラッド、それは私の意図ではありませんでした、そしてあなたは正しいです、上限で十分です。それをアップデートとして追加します...
Nivas

私はこれを簡潔できれいな+1としています
pimvdb '13

3

正確には何をしているの//do stuffですか?

あなたは次のようなことができるかもしれません:

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

3

まだテストされておらず、これが機能するかどうか不明ですが、の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;
}

2

これは別のオプションです。

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

1

承認された回答を更新しています(まだコメントできません)。2016年1月12日の時点で、クロムでデモjsfiddleを使用する場合、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

15ms「if-immediate」15ms「if-indirect」15ms「switch-immediate」37ms「switch-range」28ms「switch-range2」35ms「switch-indirect-array」29ms「array-linear-switch」 62ms "array-binary-switch"終了1.00(15ms)if-immediate 1.00(15ms)if-indirect 1.00(15ms)switch-immediate 2.47(37ms)switch-range 1.87(28ms)switch-range2 2.33(35ms)switch- indirect-array 1.93(29ms)array-linear-switch 4.13(62ms)array-binary-switch chromeバージョン48.0.2564.109(64-bit)mac os x 10.11.3
RenaissanceProgrammer

Mac OS X上のATM Safari 9.XとSafari ios 9.3の「if-immediate」が勝者です
RenaissanceProgrammer

1
1ミリ秒の差は気にするには小さすぎます。それは各テストの実行とはさらに異なります。重要なのは、意味のあるコーディングスタイルを使用し、マイクロ最適化を試みないことです。
いくつかの

1

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

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

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++});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.