Instruction switch pour supérieur à / inférieur à


230

donc je veux utiliser une instruction switch comme celle-ci:

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

Maintenant, je sais que ces déclarations ( <1000) ou ( >1000 && <2000) ne fonctionneront pas (pour différentes raisons, évidemment). Ce que je demande, c'est la manière la plus efficace de le faire. Je déteste utiliser 30 ifinstructions, donc je préfère utiliser la syntaxe de commutateur. Puis-je faire quelque chose?


5
vos pas sont-ils réguliers? Je veux dire, si vous divisez scrollLeft par 1000, vous pouvez basculer 1, 2, 3 ...
IcanDivideBy0

Vous pouvez peut-être créer un tableau trié qui mappe une plage de conditions avec l'opération correspondante et y appliquer une recherche binaire. Ou si vos conditions sont assez régulières, vous pouvez appeler directement your_mapper_object[scrollLeft / SOME_CONST], en supposant your_mapper_objectque cela ressemble à quelque chose {1: some_func, 2: another_func, ...}. Et dans ce cas, vous pouvez également utiliser switch.
Overmind Jiang

Réponses:


732

Quand j'ai regardé les solutions dans les autres réponses, j'ai vu des choses que je sais néfastes pour les performances. J'allais les mettre dans un commentaire mais j'ai pensé qu'il valait mieux le comparer et partager les résultats. Vous pouvez le tester vous-même . Voici mes résultats (ymmv) normalisés après l'opération la plus rapide dans chaque navigateur (multipliez le temps 1.0 par la valeur normalisée pour obtenir le temps absolu en ms).

                    Nœud Safari Firefox Firefox MSIE
-------------------------------------------------- -----------------
1.0 temps 37ms 73ms 68ms 184ms 73ms 21ms
si immédiat 1,0 1,0 1,0 2,6 1,0 1,0
si-indirect 1,2 1,8 3,3 3,8 2,6 1,0
commutation immédiate 2,0 1,1 2,0 1,0 2,8 1,3
plage de commutation 38,1 10,6 2,6 7,3 20,9 10,4
gamme de commutateurs2 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
Commutateur linéaire de réseau 3,6 4,1 4,5 10,0 4,7 2,7
commutateur binaire de matrice 7,8 6,7 9,5 16,0 15,0 4,9

Testez où effectué sur Windows 7 32 bits avec les versions suivantes: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 . Node a été exécuté sur une boîte Linux 64 bits car la résolution du minuteur sur Node.js pour Windows était de 10 ms au lieu de 1 ms.

si-immédiat

C'est le plus rapide dans tous les environnements testés, sauf dans ... drumroll MSIE! (surprise Surprise). Il s'agit de la méthode recommandée pour l'implémenter.

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

si-indirect

Il s'agit d'une variante de switch-indirect-arraymais avec des ifinstructions à la place et qui fonctionne beaucoup plus rapidement que switch-indirect-arraydans presque tous les environnements testés.

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

interrupteur immédiat

C'est assez rapide dans tous les environnements testés, et en fait le plus rapide dans MSIE. Cela fonctionne lorsque vous pouvez faire un calcul pour obtenir un index.

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

gamme de commutateurs

C'est environ 6 à 40 fois plus lent que le plus rapide dans tous les environnements testés, sauf pour Opera où cela prend environ une fois et demie plus de temps. C'est lent car le moteur doit comparer la valeur deux fois pour chaque cas. Étonnamment, cela prend presque 40 fois plus de temps à Chrome pour terminer cette opération que l'opération la plus rapide de Chrome, tandis que MSIE ne prend que 6 fois plus de temps. Mais le décalage horaire réel n'était que de 74 ms en faveur de MSIE à 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;
}

gamme de commutateurs2

Il s'agit d'une variante de switch-rangemais avec une seule comparaison par cas et donc plus rapide, mais toujours très lente sauf dans Opera. L'ordre de la déclaration de cas est important car le moteur testera chaque cas dans l'ordre du code source 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-indirect-array

Dans cette variante, les plages sont stockées dans un tableau. C'est lent dans tous les environnements testés et très lent dans 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;
}

tableau-recherche linéaire

Il s'agit d'une combinaison d'une recherche linéaire de valeurs dans un tableau et de l'instruction switch avec des valeurs fixes. La raison pour laquelle vous voudrez peut-être l'utiliser est que les valeurs ne sont pas connues avant l'exécution. Il est lent dans tous les environnements testés et prend près de 10 fois plus de temps dans 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;
}

tableau-commutateur-binaire

Il s'agit d'une variante de array-linear-switchmais avec une recherche binaire. Malheureusement, elle est plus lente que la recherche linéaire. Je ne sais pas si c'est mon implémentation ou si la recherche linéaire est plus optimisée. Il se peut également que l'espace de clés soit trop petit.

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

Conclusion

Si les performances sont importantes, utilisez if-statements ou switchavec des valeurs immédiates.


128
Il est rare de voir une réponse avec autant de détails et de structure bien rangée. Big +1
Rick Donohoe

10
Big +1 pour l'explication du côté performances de ce problème!
Zoltán Schmidt

16
C'est la raison pour laquelle stackoverflow est l'un des meilleurs endroits pour obtenir des réponses. Ceci est une réponse "intemporelle", excellent travail et merci pour le jsfiddle!
Jessy

1
grt info & explication
JayKandari

3
J'aimerais vraiment pouvoir +2, une réponse si détaillée!
Kaspar Lee

96

Une alternative:

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

Démo: http://jsfiddle.net/UWYzr/


4
il s'agit d'une solution plus valable. +1
IcanDivideBy0

1
N'est-ce pas la même chose que if(...) else if(...)? Cela évite, ifmais ne ressemble pas vraiment à un joli remplacement pour moi.
pimvdb

7
Bien qu'élégant à coder, il nuit aux performances. Il est presque 30 fois plus lent dans Chrome que dans les ifdéclarations. Voir ma réponse ici
certains

1
Cependant, une telle pénalité de performance est négligeable lorsque les données traitées ne sont pas volumineuses et peut-être que c'est une fonction appliquée uniquement, comme la validation d'une entrée utilisateur unique, puis la lisibilité est choisie plutôt que les performances dans ce cas.
Jesús Franco

1
Ceci est exactement ce que je cherchais. Merci!
Ami Schreiber

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

Fonctionne uniquement si vous avez des étapes régulières ...

EDIT: puisque cette solution ne cesse de recevoir des votes positifs, je dois vous conseiller que la solution de mofolo est bien meilleure


1
J'ai utilisé Math.round(scrollLeft/1000)d'ailleurs.
switz

@Switz - Gardez juste à l'esprit que 999 <1000 tombe dans le cas 0 mais Math.round (999/1000) tombe dans le cas 1. En outre, il y a une faute de frappe ci-dessus, dans ce cas 1 est> = 1000, pas seulement> 1000 .
Igor

Le seul problème avec la solution de mofolo est qu'elle est environ 30 fois plus lente dans Chrome que celle d'IcanDivideBy0. Voir ma réponse ci-dessous.
certains

6

Vous pouvez créer un objet personnalisé avec les critères et la fonction correspondant aux critères

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

Définissez les fonctions pour ce que vous voulez faire dans ces cas (définissez function1, function2 etc.)

Et "évaluer" les règles

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

Remarque

Je déteste utiliser 30 if

Plusieurs fois si les déclarations sont plus faciles à lire et à maintenir. Je recommanderais ce qui précède uniquement lorsque vous avez beaucoup de conditions et une possibilité de beaucoup de croissance à l'avenir.

Mise à jour
Comme @Brad l'a souligné dans les commentaires, si les conditions s'excluent mutuellement (une seule d'entre elles peut être vraie à la fois), la vérification de la limite supérieure devrait être suffisante:

if(scrollLeft < oneRule.upperLimit)

à condition que les conditions soient définies par ordre croissant (d'abord la plus basse 0 to 1000, puis 1000 to 2000par exemple)


action=function1- ne devraient-ils pas être des deux points? ;-) - Vous pouvez également refactoriser cela pour n'avoir qu'une limite supérieure car, en raison du processus d'élimination, vous ne pouvez pas appartenir à deux groupes - sauf si c'était votre intention (d'avoir plusieurs actions possibles).
Brad Christie

@Brad Christie Of Course
Nivas

@Brad, non, ce n'était pas mon intention, et vous avez raison, la limite supérieure devrait suffire. Ajoutera cela comme une mise à jour ...
Nivas

Je trouve celui-ci concis et propre +1
pimvdb

3

Que faites-vous exactement //do stuff?

Vous pourrez peut-être faire quelque chose comme:

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

3

Non testé et incertain si cela fonctionnera, mais pourquoi ne pas en faire quelques-uns if statementsavant, pour définir des variables pour le 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

Ceci est une autre option:

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

1

Mise à jour de la réponse acceptée (ne peut pas encore commenter). Depuis le 1/12/16 utilisant la démo jsfiddle en chrome, switch-immediate est la solution la plus rapide.

Résultats: Résolution temporelle: 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

Fini

 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

cela dépend vraiment - 15 ms "si immédiat" 15 ms "si indirect" 15 ms "commutateur immédiat" 37 ms "gamme de commutateurs" 28 ms "commutateur-gamme2" 35 ms "commutateur-indirect-tableau" 29 ms "tableau-linéaire-commutateur" 62 ms "matrice-commutateur-binaire" Terminé 1,00 (15ms) si-immédiat 1,00 (15ms) si-indirect 1,00 (15ms) commutateur-immédiat 2,47 (37ms) plage de commutation 1,87 (28ms) plage de commutation2 2,33 (35ms) commutateur- indirect-array 1.93 (29ms) array-linear-switch 4.13 (62ms) array-binary-switch chrome Version 48.0.2564.109 (64-bit) mac os x 10.11.3
RenaissanceProgrammer

ATM Safari 9.X sur Mac OS x et Safari ios 9.3, "si immédiat" est le grand gagnant
RenaissanceProgrammer

1
Une différence de 1 ms est trop peu pour s'en soucier. Elle varie davantage que celle de chaque test. Le point est: utilisez le style de codage qui a du sens et n'essayez pas de micro-optimiser.
du

1

Dans mon cas (codage couleur d'un pourcentage, rien de critique pour les performances), j'ai rapidement écrit ceci:

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

Je déteste utiliser 30 if

J'ai eu la même situation ces derniers temps, c'est comme ça que je l'ai résolu:

avant:

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

aprè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]});

Et si vous définissez "1, 2, 3, 4, 5", cela peut être encore plus simple:

var scales = [4, 6, 9, 12, 18];
scales.forEach(function(el){if(wind_speed >= el) scale++});
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.