Cambiar la instrucción para mayor que / menor que

230

entonces quiero usar una declaración de cambio como esta:

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

Ahora sé que cualquiera de esas declaraciones ( <1000) o ( >1000 && <2000) no funcionará (por diferentes razones, obviamente). Lo que pregunto es la forma más eficiente de hacer precisamente eso. Odio usar 30 ifdeclaraciones, así que prefiero usar la sintaxis de cambio. ¿Hay algo que pueda hacer?

Switz
fuente
55
¿Tus pasos son regulares? Quiero decir, si divides scrollLeft entre 1000, puedes cambiar 1, 2, 3 ...
IcanDivideBy0
Tal vez podría hacer una matriz ordenada que mapee un rango de condiciones con la operación correspondiente y aplicar una búsqueda binaria en él. O si sus condiciones son lo suficientemente regulares, puede llamar directamente your_mapper_object[scrollLeft / SOME_CONST], suponiendo que your_mapper_objectsea ​​algo así {1: some_func, 2: another_func, ...}. Y en este caso también podría usar el interruptor.
Overmind Jiang

Respuestas:

732

Cuando miré las soluciones en las otras respuestas, vi algunas cosas que sé que son malas para el rendimiento. Iba a ponerlos en un comentario, pero pensé que era mejor compararlo y compartir los resultados. Puedes probarlo tú mismo . A continuación se muestran mis resultados (ymmv) normalizados después de la operación más rápida en cada navegador (multiplique el tiempo 1.0 con el valor normalizado para obtener el tiempo absoluto en ms).

                    Chrome Firefox Opera MSIE Safari Node
-------------------------------------------------- -----------------
1.0 tiempo 37ms 73ms 68ms 184ms 73ms 21ms
si es inmediato 1.0 1.0 1.0 2.6 1.0 1.0
si indirecto 1.2 1.8 3.3 3.8 2.6 1.0
cambio inmediato 2.0 1.1 2.0 1.0 2.8 1.3
rango de conmutación 38.1 10.6 2.6 7.3 20.9 10.4
rango de conmutación2 31.9 8.3 2.0 4.5 9.5 6.9
switch-indirecta-matriz 35.2 9.6 4.2 5.5 10.7 8.6
matriz-interruptor-lineal 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

La prueba se realizó en Windows 7 de 32 bits con las siguientes versiones: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 . Node se ejecutó en una caja Linux de 64 bits porque la resolución del temporizador en Node.js para Windows fue de 10 ms en lugar de 1 ms.

si es inmediato

Este es el más rápido en todos los entornos probados, excepto en ... drumroll MSIE! (sorpresa sorpresa). Esta es la forma recomendada de implementarlo.

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

si indirecto

Esta es una variante de switch-indirect-arraypero con ifdeclaraciones en su lugar y funciona mucho más rápido que switch-indirect-arrayen casi todos los entornos probados.

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

cambio inmediato

Esto es bastante rápido en todos los entornos probados, y en realidad es el más rápido en MSIE. Funciona cuando puede hacer un cálculo para obtener un índice.

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

rango de conmutación

Esto es aproximadamente de 6 a 40 veces más lento que el más rápido en todos los entornos probados, excepto Opera, donde dura aproximadamente una vez y media más. Es lento porque el motor tiene que comparar el valor dos veces para cada caso. Sorprendentemente, Chrome tarda casi 40 veces más en completar esto en comparación con la operación más rápida en Chrome, mientras que MSIE solo tarda 6 veces más. Pero la diferencia de tiempo real fue de solo 74 ms a favor de MSIE a 1337 ms (!).

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

switch-range2

Esta es una variante de switch-rangepero con solo una comparación por caso y, por lo tanto, más rápida, pero aún muy lenta, excepto en Opera. El orden de la declaración del caso es importante ya que el motor probará cada caso en el orden del código fuente ECMAScript262: 5 12.11

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

conmutador-matriz-indirecta

En esta variante, los rangos se almacenan en una matriz. Esto es lento en todos los entornos probados y muy lento en 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;
}

array-linear-search

Esta es una combinación de una búsqueda lineal de valores en una matriz y la instrucción de cambio con valores fijos. La razón por la que uno podría querer usar esto es cuando los valores no se conocen hasta el tiempo de ejecución. Es lento en todos los entornos probados y tarda casi 10 veces más en MSIE.

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

matriz-interruptor-binario

Esta es una variante de array-linear-switchpero con una búsqueda binaria. Lamentablemente es más lento que la búsqueda lineal. No sé si es mi implementación o si la búsqueda lineal está más optimizada. También podría ser que el espacio de teclas sea demasiado pequeño.

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

Conclusión

Si el rendimiento es importante, use ifdeclaraciones o switchcon valores inmediatos.

algunos
fuente
128
Es raro ver una respuesta con tanto detalle y estructura ordenada. Big +1
Rick Donohoe
10
¡Gran +1 para la explicación del lado del rendimiento de este problema!
Zoltán Schmidt
16
Esta es la razón por la cual stackoverflow es uno de los mejores lugares para obtener respuestas. Esta es una respuesta "intemporal", excelente trabajo y gracias por el jsfiddle
Jessy
1
información y explicación de
grt
3
Realmente desearía poder +2, ¡una respuesta tan detallada!
Kaspar Lee
96

Una alternativa:

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

Demostración: http://jsfiddle.net/UWYzr/

trabajo
fuente
44
Esta es una solución más valiosa. +1
IcanDivideBy0
1
¿No es esto lo mismo que if(...) else if(...)? Esto sí lo evita, ifpero no me parece un buen reemplazo.
pimvdb
77
Aunque elegante para codificar, perjudica el rendimiento. Es casi 30 veces más lento en Chrome que el uso de ifdeclaraciones. Vea mi respuesta aquí
algunos
1
Sin embargo, dicha penalización de rendimiento es insignificante cuando los datos que se manejan no son grandes y tal vez es solo una función aplicada, como validar una sola entrada del usuario, luego se elige la legibilidad en lugar del rendimiento en tal caso.
Jesús Franco
1
Esto es exactamente lo que estaba buscando. ¡Gracias!
Ami Schreiber
23
switch (Math.floor(scrollLeft/1000)) {
  case 0: // (<1000)
   //do stuff
   break;
  case 1: // (>=1000 && <2000)
   //do stuff;
   break;
}

Solo funciona si tienes pasos regulares ...

EDITAR: dado que esta solución sigue recibiendo votos positivos, debo advertir que la solución de mofolo es mucho mejor

IcanDivideBy0
fuente
1
Yo solía Math.round(scrollLeft/1000)por cierto.
switz
@Switz: solo tenga en cuenta que 999 <1000 cae en el caso 0 pero Math.round (999/1000) cae en el caso 1. Además, hay un error tipográfico arriba, en ese caso 1 es> = 1000, no solo> 1000 .
Igor
El único problema con la solución de mofolo es que es aproximadamente 30 veces más lento en Chrome que el de IcanDivideBy0. Se mi respuesta a continuación.
Algunos
6

Puede crear un objeto personalizado con los criterios y la función correspondiente a los criterios.

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

Defina funciones para lo que desea hacer en estos casos (defina function1, function2, etc.)

Y "evaluar" las reglas

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

Nota

Odio usar 30 declaraciones if

Muchas veces si las declaraciones son más fáciles de leer y mantener. Recomendaría lo anterior solo cuando tenga muchas condiciones y una posibilidad de mucho crecimiento en el futuro.

Actualización
Como @Brad señaló en los comentarios, si las condiciones son mutuamente excluyentes (solo una de ellas puede ser verdadera a la vez), verificar el límite superior debería ser suficiente:

if(scrollLeft < oneRule.upperLimit)

siempre que las condiciones se definan en orden ascendente (primero la más baja 0 to 1000, y luego, 1000 to 2000por ejemplo)

Nivas
fuente
action=function1¿No deberían ser estos dos puntos? ;-) - También puede refactorizar esto para que solo tenga un límite superior ya que, debido al proceso de eliminación, no puede caer dentro de dos grupos, a menos que sea su intención (tener múltiples acciones posibles).
Brad Christie
@Brad Christie, por supuesto
Nivas
@Brad, no, esa no era mi intención, y tienes razón, el límite superior debería ser suficiente. Agregará eso como una actualización ...
Nivas
Me parece conciso y limpio +1
pimvdb
3

¿Qué estás haciendo exactamente //do stuff?

Es posible que pueda hacer algo como:

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

No probado e inseguro si esto funcionará, pero ¿por qué no hacer algunos if statementsantes, para establecer variables para el 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;
}
Jason Gennaro
fuente
2

Esta es otra opción:

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

Actualizando la respuesta aceptada (no puedo comentar aún). A partir del 1/12/16 con la demo jsfiddle en Chrome, switch-inmediato es la solución más rápida.

Resultados: Resolución de tiempo: 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

Terminado

 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
jeffhale
fuente
realmente depende - 15 ms "si es inmediato" 15 ms "si es indirecto" 15 ms "conmutador inmediato" 37 ms "rango de conmutador" 28 ms "rango de conmutador2" 35 ms "conmutador-indirecto-conjunto" 29 ms "conjunto-lineal-conmutador" 62ms "array-binary-switch" Finalizado 1.00 (15ms) si-inmediato 1.00 (15ms) si-indirecto 1.00 (15ms) interruptor-inmediato 2.47 (37ms) rango-interruptor 1.87 (28ms) rango-interruptor2 2.33 (35ms) interruptor- indirecta-matriz 1.93 (29ms) matriz-lineal-switch 4.13 (62ms) matriz-binaria-switch cromo Versión 48.0.2564.109 (64-bit) mac os x 10.11.3
RenaissanceProgrammer
ATM Safari 9.X en Mac OS x y Safari ios 9.3, "if-inmediato" es el claro ganador
RenaissanceProgrammer
1
La diferencia de 1 ms es demasiado pequeña para preocuparse. Varía más que eso de cada ejecución de prueba. El punto es: use el estilo de codificación que tenga sentido, y no intente micro-optimizar.
algunos
1

En mi caso (codificación por colores de un porcentaje, nada crítico para el rendimiento), rápidamente escribí esto:

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

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

Odio usar 30 declaraciones if

Tuve la misma situación últimamente, así es como lo resolví:

antes de:

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

después:

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

Y si configura "1, 2, 3, 4, 5", entonces puede ser aún más simple:

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