大于/小于的切换语句


230

所以我想使用这样的switch语句:

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

现在我知道,这些语句(<1000)或(>1000 && <2000)都不起作用(显然是由于不同的原因)。我要问的是最有效的方法。我讨厌使用30条if语句,所以我宁愿使用switch语法。有什么我可以做的吗?


5
您的步骤是否正常?我的意思是,如果将scrollLeft除以1000,则可以切换1、2、3 ...
IcanDivideBy0 2011年

也许您可以创建一个排序数组,该数组将条件范围与相应的操作进行映射,并对其进行二分查找。或者,如果您的条件足够正常,则您可以直接致电your_mapper_object[scrollLeft / SOME_CONST],前提your_mapper_object是类似{1: some_func, 2: another_func, ...}。在这种情况下,您也可以使用开关。
压倒姜

Answers:


731

当我查看其他答案中的解决方案时,我看到了一些我所知道的不利于性能的内容。我本打算在评论中发表评论,但我认为最好对其进行基准测试并分享结果。您可以自己进行测试。下面是我在每个浏览器中以最快的速度进行操作后归一化的结果(ymmv)(将1.0的时间乘以归一化的值即可得出以毫秒为单位的绝对时间)。

                    Chrome Firefox Opera MSIE Safari节点
-------------------------------------------------- -----------------
1.0时间37ms 73ms 68ms 184ms 73ms 21ms
如果立即1.0 1.0 1.0 2.6 1.0 1.0
如果间接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.7Node在Linux 64位机上运行,​​因为Windows的Node.js上的计时器分辨率是10ms,而不是1ms。

如果立即

这是在所有经过测试的环境中最快的速度,除了... 感光鼓 MSIE!(惊喜)。这是推荐的实现方法。

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

如果间接

这是-statement 的变体,switch-indirect-array但是具有if-statement,并且比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;
}

开关范围

这比所有经过测试的环境中最快的速度慢6至40倍,Opera所需的时间大约是其一半左右。这很慢,因为引擎必须针对每种情况两次比较该值。令人惊讶的是,与Chrome中最快的操作相比,Chrome需要近40倍的时间来完成此操作,而MSIE只需6倍的时间。但是实际的时差仅为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但每个案例只有一个比较,因此比较快,但在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-statement或switch立即值。


128
如此详细和整洁的结构很难找到答案。大+1
里克·多诺霍

10
大+1,用于解释此问题的性能方面!
佐尔坦施密特

16
这就是为什么stackoverflow是最佳答案的原因之一。这是一个“永恒的”答案,非常好,感谢jsfiddle!
Jessy 2014年

1
GRT信息及explainination
JayKandari

3
我真的希望我能+2,这么详细的答案!
卡斯珀·李

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 2011年

1
这不就是一样if(...) else if(...)吗?这确实可以避免,if但对我来说听起来并不是一个很好的替代品。
pimvdb

7
虽然代码优雅,但会影响性能。在Chrome中,它的速度几乎比使用if-statement 慢30倍。见我在这里的答案
一些

1
但是,当要处理的数据不大并且可能仅应用其功能时(例如验证单个用户输入),则在这种情况下,性能的损失可以忽略不计。
赫苏斯·弗朗哥

1
这正是我想要的。谢谢!
Ami Schreiber

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-请记住,999 <1000属于情况0,而Math.round(999/1000)属于情况1。此外,上面还有一个错字,在这种情况下1> = 1000,而不仅仅是> 1000 。
伊戈尔(Igor)

mofolo解决方案的唯一问题是Chrome中的速度比IcanDivideBy0慢30倍。请在下面回答我的答案
一些

6

您可以使用条件和与条件相对应的功能来创建自定义对象

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

在这些情况下定义要执行的功能(定义功能1,功能2等)

并“评估”规则

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在评论中指出,如果条件是互斥的(一次只能满足其中之一),则检查上限应该足够:

if(scrollLeft < oneRule.upperLimit)

前提条件是按升序定义(首先是最低的0 to 1000,然后1000 to 2000是例如)


action=function1-这些不应该是冒号吗?;-)-您也可以将其重构为仅具有上限,因为由于淘汰的过程,您不能属于两个组-除非您打算这样做(可以采取多种行动)。
布拉德·克里斯蒂

@布拉德·克里斯蒂(Brad Christie)当然
Nivas

@布拉德,不,那不是我的意图,您是对的,上限应该足够。会将其添加为更新...
Nivas

我发现这是一个简洁简洁的+1
pimvdb 2011年

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

正在更新接受的答案(无法发表评论)。截至16年1月1日,在chrome中使用演示jsfiddle,立即切换是最快的解决方案。

结果:时间分辨率: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“如果立即” 15ms“如果间接” 15ms“立即开关” 37ms“切换范围” 28ms“ switch-range2” 35ms“切换间接数组” 29ms“数组线性切换” 62ms“ array-binary-switch”完成1.00(15ms)if-immediate 1.00(15ms)if-indirect 1.00(15ms)switch-inmediate 2.47(37ms)switch-range 1.87(28ms)switch-range2 2.33(35ms)switch-间接数组1.93(29ms)数组线性开关4.13(62ms)数组二进制开关chrome版本48.0.2564.109(64位)mac OS x 10.11.3
RenaissanceProgrammer

Mac OS x和Safari ios 9.3上的ATM Safari 9.X,“如果立即”是明显的赢家
RenaissanceProgrammer

1
1 ms的差异实在太小了。与每次测试运行相比,差异更大。关键是:使用有意义的编码样式,不要尝试进行微优化。
一些

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++});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.