Istruzione switch per maggiore di / minore di


230

quindi voglio usare un'istruzione switch come questa:

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

Ora so che una di queste affermazioni ( <1000) o ( >1000 && <2000) non funzionerà (per motivi diversi, ovviamente). Quello che sto chiedendo è il modo più efficiente per farlo. Odio usare 30 ifistruzioni, quindi preferirei usare la sintassi switch. C'è qualcosa che posso fare?


5
i tuoi passi sono regolari? Voglio dire, se dividi scrollLeft per 1000, puoi cambiare 1, 2, 3 ...
IcanDivideBy0

Forse potresti creare un array ordinato che mappa un intervallo di condizioni con l'operazione corrispondente e applicare una ricerca binaria su di esso. O se le tue condizioni sono abbastanza regolari, puoi chiamare direttamente your_mapper_object[scrollLeft / SOME_CONST], supponendo che your_mapper_objectsia qualcosa di simile {1: some_func, 2: another_func, ...}. E in questo caso puoi anche usare switch.
Overmind Jiang

Risposte:


732

Quando ho esaminato le soluzioni nelle altre risposte, ho visto alcune cose che so essere dannose per le prestazioni. Li avrei messi in un commento, ma ho pensato che fosse meglio confrontarlo e condividere i risultati. Puoi provarlo tu stesso . Di seguito sono riportati i miei risultati (ymmv) normalizzati dopo l'operazione più veloce in ciascun browser (moltiplicare il tempo 1.0 per il valore normalizzato per ottenere il tempo assoluto in ms).

                    Chrome Firefox Opera MSIE Safari Node
-------------------------------------------------- -----------------
1.0 tempo 37ms 73ms 68ms 184ms 73ms 21ms
if-immediate 1.0 1.0 1.0 2.6 1.0 1.0
if-indiretto 1,2 1,8 3,3 3,8 2,6 1,0
switch-immediate 2.0 1.1 2.0 1.0 2.8 1.3
campo di commutazione 38,1 10,6 2,6 7,3 20,9 10,4
campo di commutazione2 31,9 8,3 2,0 4,5 9,5 6,9
switch-indiretto-array 35,2 9,6 4,2 5,5 10,7 8,6
array-linear-switch 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

Test eseguito su Windows 7 a 32 bit con le seguenti versioni: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 . Il nodo è stato eseguito su un box Linux a 64 bit perché la risoluzione del timer su Node.js per Windows era di 10 ms anziché 1 ms.

se-immediata

Questo è il più veloce in tutti gli ambienti testati, tranne in ... rullo di tamburi MSIE! (sorpresa sorpresa). Questo è il modo raccomandato per implementarlo.

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

se-indiretta

Questa è una variante switch-indirect-arrayma con if-statements invece e funziona molto più velocemente rispetto switch-indirect-arraya quasi tutti gli ambienti testati.

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

switch-immediata

Questo è piuttosto veloce in tutti gli ambienti testati e in realtà il più veloce in MSIE. Funziona quando puoi fare un calcolo per ottenere un indice.

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

switch-gamma

Questo è circa da 6 a 40 volte più lento del più veloce in tutti gli ambienti testati ad eccezione di Opera, dove impiega circa una volta e mezza il tempo. È lento perché il motore deve confrontare il valore due volte per ogni caso. Sorprendentemente, Chrome richiede quasi 40 volte di più per completarlo rispetto all'operazione più veloce in Chrome, mentre MSIE richiede solo 6 volte di più. Ma la differenza temporale effettiva era solo 74ms a favore di MSIE a 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-intervallo2

Questa è una variante switch-rangema con un solo confronto per caso e quindi più veloce, ma comunque molto lenta tranne che in Opera. L'ordine dell'istruzione case è importante poiché il motore testerà ogni caso nell'ordine ECMAScript262: 5 12.11

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

switch-indiretta-array

In questa variante gli intervalli sono memorizzati in un array. Questo è lento in tutti gli ambienti testati e molto lento in 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-lineare di ricerca

Questa è una combinazione di una ricerca lineare di valori in un array e l'istruzione switch con valori fissi. Il motivo per cui uno potrebbe voler usare questo è quando i valori non sono noti fino al runtime. È lento in ogni ambiente testato e impiega quasi 10 volte di più in 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;
}

array-binario-switch

Questa è una variante di array-linear-switchma con una ricerca binaria. Sfortunatamente è più lento della ricerca lineare. Non so se è la mia implementazione o se la ricerca lineare è più ottimizzata. Potrebbe anche essere che lo spazio dei tasti sia troppo piccolo.

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

Conclusione

Se le prestazioni sono importanti, utilizzare if-statements o switchcon valori immediati.


128
È raro vedere una risposta con questi dettagli e una struttura così ordinata. Grande +1
Rick Donohoe,

10
Grande +1 per la spiegazione del lato prestazionale di questo problema!
Zoltán Schmidt,

16
Questo è il motivo per cui StackOverflow è uno dei posti migliori per le risposte. Questa è una risposta "senza tempo", ottimo lavoro e grazie per il jsfiddle!
Jessy,

1
grt info & spiegazione
JayKandari

3
Vorrei davvero poter +2, una risposta così dettagliata!
Kaspar Lee,

96

Un'alternativa:

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

Demo: http://jsfiddle.net/UWYzr/


4
questa è una soluzione più preziosa. +1
IcanDivideBy0

1
Non è lo stesso di if(...) else if(...)? Questo evita, ifma non sembra abbastanza un sostituto per me.
pimvdb,

7
Mentre è elegante da programmare, fa male alle prestazioni. È quasi 30 volte più lento in Chrome rispetto all'uso di if-statements. Vedi la mia risposta qui
circa il

1
Tuttavia, tale penalità di prestazione è trascurabile quando i dati gestiti non sono grandi e forse è solo una funzione applicata, come la convalida di un singolo input dell'utente, quindi in questo caso viene scelta la leggibilità anziché le prestazioni.
Jesús Franco,

1
Questo e 'esattamente quello che stavo cercando. Grazie!
Ami Schreiber,

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

Funziona solo se hai passaggi regolari ...

EDIT: poiché questa soluzione continua a ricevere voti, devo avvisare che la soluzione di mofolo è un modo migliore


1
A Math.round(scrollLeft/1000)proposito, l' ho usato .
switz,

@Switz - Tieni presente che 999 <1000 rientra nel caso 0 ma Math.round (999/1000) rientra nel caso 1. Inoltre, c'è un errore di battitura sopra, in quel caso 1 è> = 1000, non solo> 1000 .
Igor,

L'unico problema con la soluzione di mofolo è che in Chrome è circa 30 volte più lento di quello di IcanDivideBy0. Vedi la mia risposta qui sotto.
un po '

6

È possibile creare un oggetto personalizzato con i criteri e la funzione corrispondente ai criteri

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

Definire le funzioni per ciò che si desidera fare in questi casi (definire function1, function2 ecc)

E "valutare" le regole

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 usare 30 if dichiarazioni

Molte volte se le dichiarazioni sono più facili da leggere e mantenere. Consiglierei quanto sopra solo quando ci sono molte condizioni e una possibilità di crescita in futuro.

Aggiornamento
Come sottolineato da @Brad nei commenti, se le condizioni si escludono a vicenda (solo una di esse può essere vera alla volta), la verifica del limite superiore dovrebbe essere sufficiente:

if(scrollLeft < oneRule.upperLimit)

a condizione che le condizioni siano definite in ordine crescente (prima quella più bassa 0 to 1000, e poi 1000 to 2000per esempio)


action=function1- Non dovrebbero essere questi due punti? ;-) - Puoi anche riformattare questo per avere solo un limite superiore poiché, a causa del processo di eliminazione, non puoi rientrare in due gruppi - a meno che non fosse il tuo intento (avere più azioni possibili).
Brad Christie,

@Brad Christie Of Course
Nivas,

@Brad, no, non era mia intenzione, e hai ragione, il limite superiore dovrebbe essere sufficiente. Lo aggiungerò come aggiornamento ...
Nivas,

Trovo questo conciso e pulito +1
pimvdb

3

Cosa stai facendo esattamente //do stuff?

Potresti essere in grado di fare qualcosa del tipo:

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

3

Non testato e incerto se funzionerà, ma perché non farlo if statementsprima, per impostare le variabili per 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

Questa è un'altra opzione:

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

1

Aggiornamento della risposta accettata (non è ancora possibile commentare). A partire dal 1/12/16 utilizzando la demo jsfiddle in chrome, switch-immediate è la soluzione più veloce.

Risultati: risoluzione temporale: 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

Finito

 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

dipende davvero - 15ms "if-immediate" 15ms "if-indirette" 15ms "switch-immediate" 37ms "switch-range" 28ms "switch-range2" 35ms "switch-indiretto-array" 29ms "array-linear-switch" 62ms "array-binary-switch" Finito 1.00 (15ms) if-immediato 1.00 (15ms) if-indiretto 1.00 (15ms) switch-immediate 2.47 (37ms) range-switch 1.87 (28ms) range-switch2 2.33 (35ms) switch- indiretto-array 1,93 (29ms) array-linear-switch 4,13 (62ms) array-binary-switch chrome Versione 48.0.2564.109 (64-bit) mac os x 10.11.3
RenaissanceProgrammer

ATM Safari 9.X su Mac OS x e Safari ios 9.3, "if-immediate" è il chiaro vincitore
RenaissanceProgrammer

1
La differenza di 1 ms è troppo piccola per preoccuparsi. Varia di più rispetto a ciascun test. Il punto è: utilizzare lo stile di codifica che ha senso e non tentare di ottimizzare il micro.
circa

1

Nel mio caso (codifica a colori una percentuale, nulla di critico per le prestazioni), ho scritto rapidamente questo:

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

Odio usare 30 if dichiarazioni

Ultimamente ho avuto la stessa situazione, ecco come l'ho risolta:

prima:

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

dopo:

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 imposti "1, 2, 3, 4, 5", può essere ancora più semplice:

var scales = [4, 6, 9, 12, 18];
scales.forEach(function(el){if(wind_speed >= el) scale++});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.