Alternar instrução para maior que / menor que

230

então eu quero usar uma instrução switch como esta:

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

Agora eu sei que qualquer uma dessas declarações ( <1000) ou ( >1000 && <2000) não funcionará (por diferentes razões, obviamente). O que estou pedindo é a maneira mais eficiente de fazer exatamente isso. Eu odeio usar 30 ifinstruções, então prefiro usar a sintaxe do switch. Existe algo que eu possa fazer?

Switz
fonte
5
seus passos são regulares? Quero dizer, se você dividir o scrollLeft por 1000, poderá mudar 1, 2, 3 ...
IcanDivideBy0:
Talvez você possa criar uma matriz classificada que mapeie um intervalo de condições com a operação correspondente e aplicar uma pesquisa binária nela. Ou, se suas condições forem regulares o suficiente, você poderia ligar diretamente your_mapper_object[scrollLeft / SOME_CONST], assumindo que your_mapper_objecté algo parecido {1: some_func, 2: another_func, ...}. E, neste caso, você também pode usar o switch.
Overmind Jiang

Respostas:

731

Quando analisei as soluções nas outras respostas, vi algumas coisas que sei que são ruins para o desempenho. Eu os colocaria em um comentário, mas achei melhor compará-lo e compartilhar os resultados. Você pode testar você mesmo . Abaixo estão meus resultados (ymmv) normalizados após a operação mais rápida em cada navegador (multiplique o tempo 1,0 com o valor normalizado para obter o tempo absoluto em ms).

                    Nó Safari do Opera Firefox do MSIE do Chrome
-------------------------------------------------- -----------------
1.0 tempo 37ms 73ms 68ms 184ms 73ms 21ms
if-imediato 1,0 1,0 1,0 2,6 1,0 1,0
se indireto 1,2 1,8 3,3 3,8 2,6 1,0
mudança imediata 2,0 1,1 2,0 1,0 2,8 1,3
faixa de comutação 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
matriz indireta de comutação 35,2 9,6 4,2 5,5 10,7 8,6
comutador linear de matriz 3,6 4,1 4,5 10,0 4,7 2,7
comutador binário da matriz 7,8 6,7 9,5 16,0 15,0 4,9

Teste onde foi executado no Windows 7 32bit com as seguintes versões: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 . O nó foi executado em uma caixa de 64 bits do Linux porque a resolução do timer no Node.js para Windows era 10ms em vez de 1ms.

se imediato

Este é o mais rápido em todos os ambientes testados, exceto em ... drumroll MSIE! (surpresa surpresa). Esta é a maneira recomendada de implementá-lo.

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

se indireto

Essa é uma variante, switch-indirect-arraymas com, em ifvez disso, e executa muito mais rapidamente do que switch-indirect-arrayem quase todos os ambientes testados.

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

interruptor imediato

Isso é muito rápido em todos os ambientes testados e, na verdade, o mais rápido no MSIE. Funciona quando você pode fazer um cálculo para obter um índice.

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

switch-range

Isso é cerca de 6 a 40 vezes mais lento que o mais rápido em todos os ambientes testados, exceto no Opera, onde leva cerca de uma vez e meia o tempo. É lento porque o mecanismo precisa comparar o valor duas vezes para cada caso. Surpreendentemente, o Chrome leva quase 40 vezes mais tempo para concluir isso em comparação com a operação mais rápida do Chrome, enquanto o MSIE leva apenas 6 vezes mais. Mas a diferença horária real foi de apenas 74ms a favor do MSIE em 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;
}

switch-range2

Esta é uma variante de, switch-rangemas com apenas uma comparação por caso e, portanto, mais rápida, mas ainda muito lenta, exceto no Opera. A ordem da instrução do caso é importante, pois o mecanismo testará cada caso na ordem do código-fonte ECMAScript262: 5 12.11

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

matriz indireta

Nesta variante, os intervalos são armazenados em um array. Isso é lento em todos os ambientes testados e muito lento no 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;
}

pesquisa linear de matriz

Essa é uma combinação de uma pesquisa linear de valores em uma matriz e a instrução switch com valores fixos. O motivo pelo qual alguém pode querer usar isso é quando os valores não são conhecidos até o tempo de execução. É lento em todos os ambientes testados e leva quase 10 vezes mais tempo no 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;
}

comutador binário de matriz

Esta é uma variante de, array-linear-switchmas com uma pesquisa binária. Infelizmente, é mais lento que a pesquisa linear. Não sei se é minha implementação ou se a pesquisa linear é mais otimizada. Também pode ser que o espaço da chave seja pequeno.

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

Conclusão

Se o desempenho for importante, use if-statements ou switchcom valores imediatos.

alguns
fonte
128
É raro ver uma resposta com tantos detalhes e estrutura organizada. Big +1
Rick Donohoe
10
Big +1 para a explicação do lado do desempenho deste problema!
Zoltán Schmidt
16
Esse é o motivo pelo qual o stackoverflow é um dos melhores lugares para respostas. Esta é uma resposta "atemporal", ótimo trabalho e obrigado pelo jsfiddle!
precisa
1
Informações TAB & explainination
JayKandari
3
Eu realmente gostaria de poder +2, uma resposta tão detalhada!
Kaspar Lee
96

Uma alternativa:

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

Demonstração: http://jsfiddle.net/UWYzr/

labue
fonte
4
Esta é uma solução mais valiosa. 1
IcanDivideBy0
1
Não é o mesmo que if(...) else if(...)? Isso evita, ifmas não soa como um substituto bonito para mim.
Pimvdb
7
Embora seja elegante de codificar, prejudica o desempenho. É quase 30 vezes mais lento no Chrome do que usando if-statements. Veja minha resposta aqui
alguns
1
No entanto, essa penalidade de desempenho é insignificante quando os dados sendo manipulados não são grandes e, talvez, seja uma função aplicada apenas, como validar uma entrada de usuário único, então a legibilidade é escolhida em vez da performance nesse caso.
Jesús Franco
1
Era exatamente isso que eu estava procurando. Obrigado!
Ami Schreiber
23
switch (Math.floor(scrollLeft/1000)) {
  case 0: // (<1000)
   //do stuff
   break;
  case 1: // (>=1000 && <2000)
   //do stuff;
   break;
}

Só funciona se você tiver etapas regulares ...

EDIT: como esta solução continua a ser positiva, devo aconselhar que a solução da mofolo é muito melhor

IcanDivideBy0
fonte
1
Eu usei Math.round(scrollLeft/1000)pelo caminho.
switz
@Switz - Lembre-se de que 999 <1000 se enquadra no caso 0, mas Math.round (999/1000) se enquadra no caso 1. Além disso, há um erro de digitação acima; nesse caso, 1 é> = 1000, não apenas> 1000 .
Igor
O único problema da solução da mofolo é que ela é cerca de 30 vezes mais lenta no Chrome que a do IcanDivideBy0. Veja minha resposta abaixo.
alguns
6

Você pode criar um objeto personalizado com os critérios e a função correspondente aos critérios

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

Defina funções para o que você deseja fazer nesses casos (defina função1, função2 etc)

E "avaliar" as regras

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

Eu odeio usar 30 se declarações

Muitas vezes, se as instruções forem mais fáceis de ler e manter. Eu recomendaria o exposto somente quando você tiver muitas condições e uma possibilidade de muito crescimento no futuro.

Atualização
Como o @Brad apontou nos comentários, se as condições forem mutuamente exclusivas (apenas uma delas pode ser verdadeira por vez), verificar o limite superior deve ser suficiente:

if(scrollLeft < oneRule.upperLimit)

desde que as condições sejam definidas em ordem crescente (primeiro a mais baixa 0 to 1000e depois, 1000 to 2000por exemplo)

Nivas
fonte
action=function1- não deveriam ser dois pontos? ;-) - Você também pode refatorar isso para ter apenas um limite superior, pois, devido ao processo de eliminação, não pode se enquadrar em dois grupos - a menos que tenha sido sua intenção (ter várias ações possíveis).
31811 Brad Christie
@Brad Christie Of Course
Nivas
@ Brad, não, essa não era minha intenção, e você está certo, o limite superior deve ser suficiente. Acrescentar que como uma atualização ...
Nivas
Eu acho este um conciso e limpo +1
pimvdb
3

O que exatamente você está fazendo //do stuff?

Você pode fazer algo como:

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

Não testado e inseguro se isso funcionará, mas por que não fazer alguns if statementsantes, para definir variáveis ​​para o 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
fonte
2

Esta é outra opção:

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

Atualizando a resposta aceita (ainda não pode comentar). A partir de 1/12/16, usando a demo jsfiddle no chrome, switch-imediato é a solução mais rápida.

Resultados: Resolução temporal: 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

Acabado

 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
fonte
realmente depende - 15ms "se imediato" 15ms "se indireto" 15ms "comutador imediato" 37ms "comutador" 28ms "comutador2" 35ms "comutador indireto" 29ms "comutador linear linear" " 62ms "array-binary-switch" Terminado 1.00 (15ms) if-imediato 1.00 (15ms) if-indireto 1.00 (15ms) switch-imediato 2.47 (37ms) range-range 1.87 (28ms) switch-range2 2.33 (35ms) switch- switch indireto-matriz 1,93 (29ms) switch-linear-switch 4,13 (62ms) switch-binário-switch chrome Versão 48.0.2564.109 (64 bits) mac os x 10.11.3
RenaissanceProgrammer
ATM Safari 9.X no Mac OS X e Safari ios 9.3, "se-imediata" é o vencedor claro
RenaissanceProgrammer
1
1 ms de diferença é muito pouco para se preocupar. Isso varia mais do que isso em cada execução de teste. O ponto é: use o estilo de codificação que faz sentido e não tente otimizar micro.
alguns
1

No meu caso (codificando por cores uma porcentagem, nada crítico para o desempenho), escrevi rapidamente isso:

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

Eu odeio usar 30 se declarações

Ultimamente, tive a mesma situação, foi assim que resolvi:

antes:

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

depois de:

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

E se você definir "1, 2, 3, 4, 5", pode ser ainda mais simples:

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