Switch-Anweisung für größer als / kleiner als

230

Daher möchte ich eine switch-Anweisung wie die folgende verwenden:

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

Jetzt weiß ich, dass eine dieser Anweisungen ( <1000) oder ( >1000 && <2000) nicht funktioniert (natürlich aus verschiedenen Gründen). Was ich frage, ist der effizienteste Weg, genau das zu tun. Ich hasse es, 30 ifAnweisungen zu verwenden, deshalb würde ich lieber die Switch-Syntax verwenden. Kann ich irgendetwas tun?

switz
quelle
5
Sind deine Schritte regelmäßig? Ich meine, wenn Sie scrollleft von 1000 teilen, können Sie Schalter 1, 2, 3 ...
IcanDivideBy0
Vielleicht könnten Sie ein sortiertes Array erstellen, das einen Bedingungsbereich mit entsprechender Operation abbildet, und eine binäre Suche darauf anwenden. Oder wenn Ihre Bedingungen regelmäßig genug sind, können Sie direkt anrufen your_mapper_object[scrollLeft / SOME_CONST], vorausgesetzt, es your_mapper_objectist so etwas wie {1: some_func, 2: another_func, ...}. In diesem Fall können Sie auch den Schalter verwenden.
Overmind Jiang

Antworten:

731

Als ich mir die Lösungen in den anderen Antworten ansah, sah ich einige Dinge, von denen ich weiß, dass sie schlecht für die Leistung sind. Ich wollte sie in einen Kommentar einfügen, aber ich dachte, es wäre besser, sie zu bewerten und die Ergebnisse zu teilen. Sie können es selbst testen . Unten sind meine Ergebnisse (ymmv) aufgeführt, die nach dem schnellsten Vorgang in jedem Browser normalisiert wurden (multiplizieren Sie die 1,0-Zeit mit dem normalisierten Wert, um die absolute Zeit in ms zu erhalten).

                    Chrome Firefox Opera MSIE-Safari-Knoten
-------------------------------------------------- -----------------
1,0 Zeit 37 ms 73 ms 68 ms 184 ms 73 ms 21 ms
wenn sofort 1,0 1,0 1,0 2,6 1,0 1,0
if-indirekt 1,2 1,8 3,3 3,8 2,6 1,0
sofort umschalten 2.0 1.1 2.0 1.0 2.8 1.3
Schaltbereich 38,1 10,6 2,6 7,3 20,9 10,4
Schaltbereich2 31,9 8,3 2,0 4,5 9,5 6,9
Switch-Indirect-Array 35.2 9.6 4.2 5.5 10.7 8.6
Array-Linear-Schalter 3.6 4.1 4.5 10.0 4.7 2.7
Array-Binärschalter 7.8 6.7 9.5 16.0 15.0 4.9

Der Test wurde unter Windows 7 32-Bit mit den folgenden Versionen durchgeführt: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 . Node wurde auf einer Linux 64-Bit-Box ausgeführt, da die Timer-Auflösung unter Node.js für Windows 10 ms statt 1 ms betrug.

wenn sofort

Dies ist die schnellste in allen getesteten Umgebungen, außer in ... drumroll MSIE! (Überraschung Überraschung). Dies ist die empfohlene Methode zur Implementierung.

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

wenn-indirekt

Dies ist eine Variante von, switch-indirect-arrayaber ifstattdessen mit -statements und arbeitet viel schneller als switch-indirect-arrayin fast allen getesteten Umgebungen.

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

sofort wechseln

Dies ist in allen getesteten Umgebungen ziemlich schnell und in MSIE tatsächlich die schnellste. Es funktioniert, wenn Sie eine Berechnung durchführen können, um einen Index zu erhalten.

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

Schaltbereich

Dies ist in allen getesteten Umgebungen etwa 6 bis 40 Mal langsamer als die schnellste, mit Ausnahme von Opera, wo es etwa eineinhalb Mal so lange dauert. Es ist langsam, weil der Motor den Wert für jeden Fall zweimal vergleichen muss. Überraschenderweise dauert Chrome fast 40 Mal länger, um dies abzuschließen, verglichen mit dem schnellsten Vorgang in Chrome, während MSIE nur 6 Mal so lange dauert. Der tatsächliche Zeitunterschied betrug jedoch nur 74 ms zugunsten von MSIE bei 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;
}

Schaltbereich2

Dies ist eine Variante von switch-rangeaber mit nur einem Vergleich pro Fall und daher schneller, aber immer noch sehr langsam, außer in Opera. Die Reihenfolge der case-Anweisung ist wichtig, da die Engine jeden Fall in der Quellcode-Reihenfolge ECMAScript262: 5 12.11 testet

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

Switch-Indirect-Array

In dieser Variante werden die Bereiche in einem Array gespeichert. Dies ist in allen getesteten Umgebungen langsam und in Chrome sehr langsam.

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-Suche

Dies ist eine Kombination aus einer linearen Suche nach Werten in einem Array und der switch-Anweisung mit festen Werten. Der Grund, warum man dies verwenden möchte, ist, wenn die Werte erst zur Laufzeit bekannt sind. Es ist in jeder getesteten Umgebung langsam und dauert in MSIE fast zehnmal so lange.

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-Binärschalter

Dies ist eine Variante von, array-linear-switchaber mit einer binären Suche. Leider ist es langsamer als die lineare Suche. Ich weiß nicht, ob es sich um meine Implementierung handelt oder ob die lineare Suche optimierter ist. Es kann auch sein, dass der Schlüsselbereich zu klein ist.

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

Fazit

Wenn die Leistung wichtig ist, verwenden Sie if-Statements oder switchmit unmittelbaren Werten.

etwas
quelle
128
Es ist selten, eine Antwort mit so vielen Details und einer ordentlichen Struktur zu sehen. Big +1
Rick Donohoe
10
Big +1 für die Erklärung der Leistungsseite dieses Problems!
Zoltán Schmidt
16
Dies ist der Grund, warum Stackoverflow einer der besten Orte für Antworten ist. Dies ist eine "zeitlose" Antwort, tolle Arbeit und danke für die jsfiddle!
Jessy
1
GRT Info & Erklärung
JayKandari
3
Ich wünschte wirklich, ich könnte +2, eine so detaillierte Antwort!
Kaspar Lee
96

Eine Alternative:

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

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

labue
quelle
4
Dies ist eine wertvollere Lösung. +1
IcanDivideBy0
1
Ist das nicht genauso wie if(...) else if(...)? Das vermeidet if, klingt aber für mich nicht nach einem hübschen Ersatz.
Pimvdb
7
Das Codieren ist zwar elegant, beeinträchtigt jedoch die Leistung. In Chrome ist es fast 30-mal langsamer als bei Verwendung von if-statements. Siehe meine Antwort hier
einige
1
Ein solcher Leistungsverlust ist jedoch vernachlässigbar, wenn die verarbeiteten Daten nicht groß sind und möglicherweise nur eine Funktion angewendet wird, z. B. die Validierung einer einzelnen Benutzereingabe. In diesem Fall wird eher die Lesbarkeit als die Leistung gewählt.
Jesús Franco
1
Genau das habe ich gesucht. Vielen Dank!
Ami Schreiber
23
switch (Math.floor(scrollLeft/1000)) {
  case 0: // (<1000)
   //do stuff
   break;
  case 1: // (>=1000 && <2000)
   //do stuff;
   break;
}

Funktioniert nur, wenn Sie regelmäßige Schritte haben ...

EDIT: Da diese Lösung immer mehr Stimmen erhält, muss ich raten, dass die Lösung von mofolo viel besser ist

IcanDivideBy0
quelle
1
Ich habe es Math.round(scrollLeft/1000)übrigens benutzt.
Switz
@Switz - Denken Sie daran, dass 999 <1000 in Fall 0 fällt, Math.round (999/1000) jedoch in Fall 1. Außerdem gibt es oben einen Tippfehler, in diesem Fall ist 1> = 1000, nicht nur> 1000 .
Igor
Das einzige Problem mit der Lösung von mofolo ist, dass es in Chrome etwa 30-mal langsamer ist als das von IcanDivideBy0. Siehe meine Antwort unten.
einige
6

Sie können ein benutzerdefiniertes Objekt mit den Kriterien und der Funktion erstellen, die den Kriterien entsprechen

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

Definieren Sie Funktionen für das, was Sie in diesen Fällen tun möchten (definieren Sie Funktion1, Funktion2 usw.).

Und "bewerte" die Regeln

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

Hinweis

Ich hasse es, 30 if-Anweisungen zu verwenden

Oft, wenn Aussagen leichter zu lesen und zu pflegen sind. Ich würde das oben Genannte nur empfehlen, wenn Sie viele Bedingungen und die Möglichkeit eines großen Wachstums in der Zukunft haben.

Aktualisieren
Wie @Brad in den Kommentaren hervorhob, sollte die Überprüfung der Obergrenze ausreichend sein, wenn sich die Bedingungen gegenseitig ausschließen (es kann jeweils nur eine zutreffen):

if(scrollLeft < oneRule.upperLimit)

vorausgesetzt , die Bedingungen sind in aufsteigender Reihenfolge definiert (zuerst die niedrigste 0 to 1000und dann 1000 to 2000zum Beispiel)

Nivas
quelle
action=function1- sollten das nicht Doppelpunkte sein? ;-) - Sie können dies auch so umgestalten, dass es nur eine Obergrenze gibt, da Sie aufgrund des Eliminierungsprozesses nicht in zwei Gruppen fallen können - es sei denn, dies war Ihre Absicht (mehrere Aktionen möglich zu haben).
Brad Christie
@ Brad Christie natürlich
Nivas
@Brad, nein das war nicht meine Absicht, und du hast recht, die Obergrenze sollte ausreichen. Wird das als Update hinzufügen ...
Nivas
Ich finde dieses prägnant und sauber +1
pimvdb
3

In was genau machst du? //do stuff ?

Möglicherweise können Sie Folgendes tun:

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

Ungetestet und unsicher, ob dies funktioniert, aber warum nicht if statementsvorher ein paar , um Variablen für die zu setzen 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
quelle
2

Dies ist eine weitere Option:

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

Aktualisieren der akzeptierten Antwort (kann noch nicht kommentieren). Ab dem 12.01.16 mit der Demo jsfiddle in Chrome ist Switch-Instant die schnellste Lösung.

Ergebnisse: Zeitauflösung: 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

Fertig

 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
quelle
es kommt wirklich darauf an - 15 ms "wenn sofort" 15 ms "wenn indirekt" 15 ms "sofort wechseln" 37 ms "Schaltbereich" 28 ms "Schaltbereich2" 35 ms "Schalter indirekter Array" 29 ms "Array-linearer Schalter" 62ms "Array-Binär-Switch" Fertig 1,00 (15ms) wenn-sofort 1,00 (15ms) wenn-indirekt 1,00 (15ms) Switch-sofort 2,47 (37ms) Switch-Bereich 1,87 (28ms) Switch-Bereich2 2,33 (35ms) Switch- Indirektes Array 1,93 (29 ms) Array-Linear-Switch 4,13 (62 ms) Array-Binär-Switch Chrome Version 48.0.2564.109 (64-Bit) Mac OS X 10.11.3
RenaissanceProgrammer
ATM Safari 9.X unter Mac OS x und Safari ios 9.3, "wenn sofort" ist der klare Gewinner
RenaissanceProgrammer
1
1 ms Unterschied ist zu gering, um sich darum zu kümmern. Es variiert mehr als das von jedem Testlauf. Der Punkt ist: Verwenden Sie den sinnvollen Codierungsstil und versuchen Sie nicht, eine Mikrooptimierung vorzunehmen.
einige
1

In meinem Fall (prozentuale Farbcodierung, nichts Leistungskritisches) schrieb ich schnell Folgendes:

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

Ich hasse es, 30 if-Anweisungen zu verwenden

Ich hatte in letzter Zeit die gleiche Situation, so habe ich sie gelöst:

Vor:

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

nach dem:

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

Und wenn Sie "1, 2, 3, 4, 5" einstellen, kann es noch einfacher sein:

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