web-dev-qa-db-fra.com

Comment raccourcir mes déclarations conditionnelles

J'ai une très longue déclaration conditionnelle comme celle-ci:

if(test.type == 'itema' || test.type == 'itemb' || test.type == 'itemc' || test.type == 'itemd'){
    // do something.
}

Je me demandais si je pouvais reformuler cette expression/déclaration en une forme plus concise.

Une idée sur comment y parvenir?

153
FlyingCat

Mettez vos valeurs dans un tableau et vérifiez si votre élément est dans le tableau:

if ([1, 2, 3, 4].includes(test.type)) {
    // Do something
}

Si un navigateur que vous supportez n'a pas la méthode Array#includes , vous pouvez utiliser this polyfill .


Brève explication du raccourci tilde ~:

Mise à jour: Puisque nous avons maintenant la méthode includes, utiliser le hack ~ Est inutile. Conservez-le simplement ici pour les personnes intéressées à savoir comment cela fonctionne et/ou qui l'ont rencontré dans le code d'un autre utilisateur.

Au lieu de vérifier si le résultat de indexOf est >= 0, Il existe un joli petit raccourci:

if ( ~[1, 2, 3, 4].indexOf(test.type) ) {
    // Do something
}

Voici le violon: http://jsfiddle.net/HYJvK/

Comment cela marche-t-il? Si un élément est trouvé dans le tableau, indexOf renvoie son index. Si l'élément n'a pas été trouvé, il retournera -1. Sans entrer trop dans les détails, le ~ Est un opérateur bitwise NOT , qui renvoie 0 Uniquement pour -1.

J'aime utiliser le raccourci ~, Car il est plus succinct que de faire une comparaison sur la valeur de retour. J'aimerais que JavaScript ait une fonction in_array Qui retourne directement un booléen (semblable à PHP), mais ce n'est que des voeux pieux ( Update: cela s’appelle maintenant _ C’est appelé includes. Voir ci-dessus). Notez que inArray de jQuery, tout en partageant la signature de méthode de PHP, imite en fait la fonctionnalité native indexOf (utile dans différents cas, si l'index correspond vraiment à ce que vous recherchez).

Remarque importante: L'utilisation du raccourci tilde semble faire l'objet d'une controverse, car certains avec véhémence pensent que le code n'est pas suffisamment clair et doit être évité à tout prix (voir les commentaires sur cette réponse). Si vous partagez leurs sentiments, vous devriez vous en tenir à la solution .indexOf(...) >= 0.


Une petite explication plus longue:

Les entiers en JavaScript sont signés, ce qui signifie que le bit le plus à gauche est réservé en tant que bit de signe; un drapeau pour indiquer si le nombre est positif ou négatif, avec un 1 négatif.

Voici quelques exemples de nombres positifs au format binaire 32 bits:

1 :    00000000000000000000000000000001
2 :    00000000000000000000000000000010
3 :    00000000000000000000000000000011
15:    00000000000000000000000000001111

Maintenant, voici ces mêmes chiffres, mais négatifs:

-1 :   11111111111111111111111111111111
-2 :   11111111111111111111111111111110
-3 :   11111111111111111111111111111101
-15:   11111111111111111111111111110001

Pourquoi de telles combinaisons étranges pour les nombres négatifs? Simple. Un nombre négatif est simplement l'inverse du nombre positif + 1; ajouter le nombre négatif au nombre positif devrait toujours donner 0.

Pour comprendre cela, faisons de l'arithmétique binaire simple.

Voici comment nous ajouterions -1 À +1:

   00000000000000000000000000000001      +1
+  11111111111111111111111111111111      -1
-------------------------------------------
=  00000000000000000000000000000000       0

Et voici comment nous ajouterions -15 À +15:

   00000000000000000000000000001111      +15
+  11111111111111111111111111110001      -15
--------------------------------------------
=  00000000000000000000000000000000        0

Comment obtenons-nous ces résultats? En faisant des ajouts réguliers, comme nous l’avons appris à l’école: vous commencez par la colonne la plus à droite, et vous additionnez toutes les lignes. Si la somme est supérieure au plus grand nombre à un chiffre (qui, en décimal, est 9, Mais en binaire, 1), Nous reportons le reste à la colonne suivante.

Maintenant, comme vous le remarquerez, lors de l'ajout d'un nombre négatif à son nombre positif, la colonne la plus à droite qui n'est pas entièrement composée de 0 Aura toujours deux 1 S, qui, une fois ajoutées, se traduira par 2. La représentation binaire de deux étant 10, Nous reportons le 1 À la colonne suivante et plaçons un 0 Pour le résultat dans la première colonne. Toutes les autres colonnes à gauche ont une seule ligne avec un 1, Ainsi le 1 Reporté de la colonne précédente s'ajoutera à 2, Qui sera ensuite reporté ... Ce processus se répète jusqu'à ce que nous arrivions à la colonne la plus à gauche, où le 1 À reporter n'a nulle part où aller, il déborde et se perd, et nous reste avec 0 Est partout.

Ce système s'appelle 2's Complement . Vous pouvez en savoir plus à ce sujet ici:

représentation du complément à 2 pour les entiers signés.


Maintenant que le cours intensif du complément à 2 est terminé, vous remarquerez que -1 Est le seul nombre dont la représentation binaire est 1 Dans son ensemble.

À l'aide de l'opérateur NOT au niveau du bit ~, Tous les bits d'un nombre donné sont inversés. Le seul moyen de récupérer 0 Après avoir inversé tous les bits est de commencer par 1.

Donc, tout ceci était une façon longue de dire que ~n Ne renverra que 0 Si n est -1.

240
Joseph Silber

Vous pouvez utiliser l'instruction switch avec fall thru:

switch (test.type) {

  case "itema":
  case "itemb":
  case "itemc":
  case "itemd":
    // do something
}
242
Yuriy Galanter

Utilisation de la science: vous devriez faire ce que dit idfah et ceci pour une vitesse plus rapide tout en gardant le code court:

THIS IS PLUS RAPIDE QUE ~ Méthode

var x = test.type;
if (x == 'itema' ||
    x == 'itemb' ||
    x == 'itemc' ||
    x == 'itemd') {
    //do something
}

http://jsperf.com/if-statements-test-techsin enter image description here (Jeu supérieur: Chrome, Jeu inférieur: Firefox)

Conclusion:

Si les possibilités sont peu nombreuses et que vous savez que certaines sont plus susceptibles de se produire que vous obtenez une performance maximale sur if ||, switch fall through et if(obj[keyval]).

Si les possibilités sont nombreuses , et n'importe lequel d'entre eux pourrait être le plus important l'un, en d'autres termes, vous ne pouvez pas savoir lequel est le plus susceptible de se produire que vous obtenez le plus de performances grâce à la recherche d'objet if(obj[keyval]) et regex si cela convient.

http://jsperf.com/if-statements-test-techsin/12

je mettrai à jour si quelque chose de nouveau arrive.

62
Muhammad Umer

Si vous comparez des chaînes et qu'il existe un modèle, envisagez d'utiliser des expressions régulières.

Sinon, j'imagine que tenter de le raccourcir obscurcira votre code. Pensez simplement à envelopper les lignes pour le rendre joli.

if (test.type == 'itema' ||
    test.type == 'itemb' ||
    test.type == 'itemc' ||
    test.type == 'itemd') {
    do something.
}
32
idfah
var possibilities = {
  "itema": 1,
  "itemb": 1,
  "itemc": 1,
…};
if (test.type in possibilities) { … }

Utiliser un objet comme un tableau associatif est une chose assez commune, mais comme JavaScript n'a pas de jeu natif, vous pouvez également utiliser des objets comme jeux bon marché.

16
kojiro
if( /^item[a-d]$/.test(test.type) ) { /* do something */ }

ou si les articles ne sont pas aussi uniformes, alors:

if( /^(itema|itemb|itemc|itemd)$/.test(test.type) ) { /* do something */ }
15
Matt

Excellentes réponses, mais vous pourriez rendre le code beaucoup plus lisible en en incorporant un dans une fonction.

Ceci est complexe si, lorsque vous (ou quelqu'un d'autre) lisez le code dans un délai de quelques années, vous parcourrez la section pour comprendre ce qui se passe. Une déclaration avec ce niveau de logique métier vous fera trébucher pendant quelques secondes pendant que vous définissez ce que vous testez. Où un code comme celui-ci vous permettra de continuer à balayer.

if(CheckIfBusinessRuleIsTrue())
{
    //Do Something
}

function CheckIfBusinessRuleIsTrue() 
{
    return (the best solution from previous posts here);
}

Attribuez un nom explicite à votre fonction pour que vous compreniez immédiatement ce que vous testez et votre code sera beaucoup plus facile à analyser et à comprendre.

10
Fran Hoey

Vous pouvez mettre toutes les réponses dans un Javascript Set et ensuite appeler .contains() sur le plateau.

Vous devez toujours déclarer tout le contenu, mais l'appel en ligne sera plus court.

Quelque chose comme:

var itemSet = new Set(["itema","itemb","itemc","itemd"]);
if( itemSet.contains( test.type ){}
4
Guido Anselmi

une autre façon ou une autre façon géniale que j'ai trouvée est-ce ...

if ('a' in oc(['a','b','c'])) { //dosomething }

function oc(a)
{
  var o = {};
  for(var i=0;i<a.length;i++)  o[a[i]]='';
  return o;
}

bien sûr, comme vous pouvez le constater, cela va encore plus loin et facilite leur logique.

http://snook.ca/archives/javascript/testing_for_a_v

en utilisant des opérateurs tels que ~ && || ((), ()) ~ ~ convient uniquement si votre code est interrompu ultérieurement. Vous ne saurez pas par où commencer. Donc, la lisibilité est BIG.

si vous devez, vous pouvez le raccourcir.

('a' in oc(['a','b','c'])) && statement;
('a' in oc(['a','b','c'])) && (statements,statements);
('a' in oc(['a','b','c']))?statement:elseStatement;
('a' in oc(['a','b','c']))?(statements,statements):(elseStatements,elseStatements);

et si vous voulez faire l'inverse

('a' in oc(['a','b','c'])) || statement;
2
Muhammad Umer

Pour de très longues listes de chaînes, cette idée économiserait quelques caractères (ne dis pas que je le recommanderais dans la vie réelle, mais cela devrait fonctionner).

Choisissez un caractère que vous ne saurez pas figurer dans votre test.type, utilisez-le comme séparateur, collez-le dans une longue chaîne et recherchez les éléments suivants:

if ("/itema/itemb/itemc/itemd/".indexOf("/"+test.type+"/")>=0) {
  // doSomething
}

Si vos chaînes sont davantage contraintes, vous pouvez même omettre les délimiteurs ...

if ("itemaitembitemcitemd".indexOf(test.type)>=0) {
  // doSomething
}

... mais vous devez faire attention aux faux positifs dans ce cas (par exemple, "embite" correspondrait dans cette version)

2
CupawnTae

Une de mes méthodes préférées pour y parvenir est avec une bibliothèque telle que underscore.js ...

var isItem = _.some(['itema','itemb','itemc','itemd'], function(item) {
    return test.type === item;
});

if(isItem) {
    // One of them was true
}

http://underscorejs.org/#some

2
jcreamer898

Utilisez simplement une instruction switch à la place de if:

switch (test.type) {

  case "itema":case "itemb":case "itemc":case "itemd":
    // do your process
  case "other cases":...:
    // do other processes
  default:
    // do processes when test.type does not meet your predictions.
}

Switch fonctionne également plus rapidement que la comparaison de nombreuses conditions dans un if

2
unmultimedio

Pour plus de lisibilité, créez une fonction pour le test (oui, une fonction sur une ligne):

function isTypeDefined(test) {
    return test.type == 'itema' ||
           test.type == 'itemb' ||
           test.type == 'itemc' ||
           test.type == 'itemd';
}

puis appelez-le:

…
    if (isTypeDefined(test)) {
…
}
...
2
zaph

Je pense qu'il y a 2 objectifs lors de l'écriture de ce type de condition si.

  1. brièveté
  2. lisibilité

En tant que tel, parfois, le n ° 1 pourrait être le plus rapide, mais je prendrai le n ° 2 pour faciliter la maintenance ultérieurement. En fonction du scénario, je choisirai souvent une variante de la réponse de Walter.

Pour commencer, j'ai une fonction disponible globalement dans le cadre de ma bibliothèque existante.

function isDefined(obj){
  return (typeof(obj) != 'undefined');
}

et puis, lorsque je souhaite exécuter une condition if similaire à la vôtre, je crée un objet avec une liste des valeurs valides:

var validOptions = {
  "itema":1,
  "itemb":1,
  "itemc":1,
  "itemd":1
};
if(isDefined(validOptions[test.type])){
  //do something...
}

Ce n'est pas aussi rapide qu'une déclaration switch/case et un peu plus bavard que certains des autres exemples, mais je reçois souvent une réutilisation de l'objet ailleurs dans le code, ce qui peut être assez pratique.

Le portage sur l'un des échantillons jsperf réalisés ci-dessus, j'ai ajouté ce test et une variante permettant de comparer les vitesses. http://jsperf.com/if-statements-test-techsin/6 La chose la plus intéressante que j'ai notée est que certains combos de test dans Firefox sont beaucoup plus rapides que même Chrome.

1
scunliffe

Cela peut être résolu avec une simple boucle for:

test = {};
test.type = 'itema';

for(var i=['itema','itemb','itemc']; i[0]==test.type && [
    (function() {
        // do something
        console.log('matched!');
    })()
]; i.shift());

Nous utilisons la première section de la boucle for pour initialiser les arguments que vous souhaitez faire correspondre, la deuxième section pour arrêter l'exécution de la boucle for et la troisième pour forcer la fin de la boucle.

1
anon