298 votes

Déclaration de commutation pour le plus grand/le moins grand

Je veux donc utiliser une instruction de commutation comme celle-ci :

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

Maintenant, je sais que l'une ou l'autre de ces déclarations ( <1000 ) ou ( >1000 && <2000 ) ne fonctionnera pas (pour des raisons différentes, évidemment). Ce que je demande, c'est la manière la plus efficace de le faire. Je déteste utiliser 30 if et je préfère donc utiliser la syntaxe switch. Y a-t-il quelque chose que je puisse faire ?

5 votes

Vos pas sont-ils réguliers ? Je veux dire, si vous divisez scrollLeft par 1000, vous pouvez passer de 1, 2, 3...

0 votes

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

934voto

some Points 18965

Lorsque j'ai regardé les solutions dans les autres réponses, j'ai vu certaines choses qui, je le sais, sont mauvaises pour les performances. J'allais les mettre dans un commentaire, mais j'ai pensé qu'il était préférable de les évaluer et de partager les résultats. Vous pouvez testez-le vous-même . Voici mes résultats (ymmv) normalisés après l'opération la plus rapide dans chaque navigateur.

Voici les résultats du 2021-MAY-05

Test

Chrome

Firefox

Opéra

Bordure

Courageux

Nœud

1.0 temps

15 ms

14 ms

17 ms

17 ms

16 ms

14 ms

si-immédiat

1.00

1.00

1.00

1.00

1.00

1.00

si-indirect

2.20

1.21

2.06

2.18

2.19

1.93

switch-immediate

2.07

1.43

1.71

1.71

2.19

1.93

gamme de commutateurs

3.60

2.00

2.47

2.65

2.88

2.86

switch-range2

2.07

1.36

1.82

1.71

1.94

1.79

commutateur-indirect-array

2.93

1.57

2.53

2.47

2.75

2.50

commutateur linéaire de réseau

2.73

3.29

2.12

2.12

2.38

2.50

commutateur binaire de réseau

5.80

6.07

5.24

5.24

5.44

5.37

Les tests de 2021 ont été effectués sur Windows 10 64bit avec les versions suivantes : Chrome 90.0.4430.212 , Firefox 89.0b13 , Opera 76.0.4017.123 , Edge 90.0.818.62 , Brave 1.24.85 y Node 16.1.0 (était géré sous WSL)

Apple ne met pas à jour Safari pour Windows Il s'agit donc toujours de la version 5.1.7. Je l'ai changé en Brave dans ce test.

Voici les résultats de 2012-septembre-04, pour une comparaison historique :

Test

Chrome

Firefox

Opéra

MSIE

Safari

Nœud

1.0 temps

37 ms

73 ms

68 ms

184 ms

73 ms

21 ms

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

switch-immediate

2.0

1.1

2.0

1.0

2.8

1.3

gamme de commutateurs

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

commutateur-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 réseau

7.8

6.7

9.5

16.0

15.0

4.9

Les tests en 2012 ont été effectués sur Windows 7 32bit avec les versions suivantes : Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 . Nœud a été exécuté sur une boîte Linux 64bit parce que la résolution du timer sur Node pour Windows était 10ms au lieu de 1ms.

si-immédiat

Cette méthode est la plus rapide dans tous les environnements testés, sauf dans ... roulement de tambour MSIE ! (surprise, surprise).

C'est la manière recommandée de le mettre en œuvre.

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-array mais avec if -et est plus rapide dans tous les moteurs testés.

En 2021, il était 20 à 120 % (2012 : 0 à 280 %) plus lent que le test le plus rapide. Chrome prend plus de temps en 2021 (2,20) qu'en 2012 (1,2).

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

Cela fonctionne lorsque vous pouvez effectuer un calcul pour obtenir un indice.

En 2021, il était de 40 à 120 % (2012 : 0 à 180 %) plus lent que le taux d'inflation. if-immediate sauf dans MSIE où il est le plus rapide.

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

gamme de commutateurs

Il est lent car le moteur doit comparer la valeur deux fois pour chaque cas.

En 2021, il était 1-2,6 fois (2012 : 1,6-38) plus lent que le test le plus rapide. Chrome a fait la plus grande amélioration, passant de 38 à 3,6, mais reste le moteur testé le plus lent.

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

Il s'agit d'une variante de switch-range mais avec une seule comparaison par cas et donc plus rapide. L'ordre de la déclaration de cas est important car le moteur testera chaque cas dans l'ordre du code source. ECMAScript 2020 13.12.9

En 2021, il était 36 à 107 % plus lent que le test le plus rapide, mais en 2012, il était 1 à 31 fois plus lent. C'est toujours Chrome qui a les plus mauvaises performances sur ce test, mais il s'est amélioré de 32 à 2 fois.

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

commutateur-indirect-array

Dans cette variante, les plages sont stockées dans un tableau.

En 2021, il était de 57 à 193 % (2012 : 3 à 35 fois) plus lent que le test le plus rapide. La performance s'est améliorée dans tous les moteurs testés, et si Chrome est toujours le plus lent, il est passé de 35 à 2,93.

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

recherche linéaire de tableaux

Dans cette variante, les plages sont stockées dans un tableau.

En 2021, il était de 57 à 193 % (2012 : 3 à 35 fois) plus lent que le test le plus rapide. La performance s'est améliorée dans tous les moteurs testés, et si Chrome est toujours le plus lent, il est passé de 35 à 2,93.

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

commutateur binaire de réseau

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

En 2021, ce rythme était 4 à 5 fois (2012 : 4 à 16) plus lent. Ne pas utiliser .

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 -déclarations ou switch avec des valeurs immédiates.

183 votes

Il est rare de voir une réponse avec autant de détails et une structure bien ordonnée. Grand +1

1 votes

@RickDonohoe Merci beaucoup !

18 votes

Big +1 pour l'explication de l'aspect performance de ce problème !

107voto

labue Points 1456

Une alternative :

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

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

4 votes

Cette solution est plus avantageuse. +1

0 votes

Une solution intéressante pour travailler avec des marches régulières et irrégulières. +1

2 votes

N'est-ce pas la même chose que if(...) else if(...) ? Cela permet d'éviter if mais ça ne ressemble pas à un joli remplacement pour moi.

24voto

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

Ne fonctionne que si vous avez des pas réguliers...

EDIT : puisque cette solution continue à recevoir des upvotes, je dois conseiller que la solution de mofolo est bien meilleur

1 votes

J'ai utilisé Math.round(scrollLeft/1000) au fait.

0 votes

@Switz - Gardez à l'esprit que 999 < 1000 tombe dans le cas 0 mais que Math.round(999/1000) tombe dans le cas 1. De plus, il y a une faute de frappe ci-dessus, dans le sens où le cas 1 est >= 1000, et pas seulement >1000.

0 votes

Le seul problème avec la solution de mofolo est qu'elle est environ 30 fois plus lente dans Chrome que celle de IcanDivideBy0. Voir mon réponse abajo.

6voto

Nivas Points 10159

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éfinir des fonctions pour ce que vous voulez faire dans ces cas (définir 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();
       }
   }
}

Note

Je déteste utiliser 30 instructions "si".

Souvent, les déclarations "if" sont plus faciles à lire et à maintenir. Je recommande l'utilisation de la méthode ci-dessus uniquement lorsque vous avez beaucoup de conditions. y une possibilité de beaucoup de la croissance à l'avenir.

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

if(scrollLeft < oneRule.upperLimit)

fourni par que les conditions sont définies par ordre croissant (la première étant la plus basse), 0 to 1000 y luego 1000 to 2000 par exemple)

0 votes

action=function1 -- ne devraient-ils pas être des deux-points ? ;-) -- Vous pouvez aussi remanier cela pour n'avoir qu'une limite supérieure car, en raison du processus d'élimination, vous ne pouvez pas tomber dans deux groupes - à moins que ce ne soit votre intention (d'avoir plusieurs actions possibles).

0 votes

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

0 votes

Je trouve celui-ci concis et propre +1

3voto

Jason Gennaro Points 20848

Je n'ai pas testé et je ne suis pas sûr que cela fonctionne, mais pourquoi ne pas en faire quelques-uns ? if statements avant, pour définir les variables de l 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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X