Vérifier l'égalité des variables par rapport à une liste de valeurs


133

Je vérifie une variable, disons foo, pour l'égalité à un certain nombre de valeurs. Par exemple,

if( foo == 1 || foo == 3 || foo == 12 ) {
    // ...
}

Le fait est qu'il s'agit plutôt de code pour une tâche aussi triviale. J'ai trouvé ce qui suit:

if( foo in {1: 1, 3: 1, 12: 1} ) {
    // ...
}

mais aussi cela ne me plaît pas complètement, car je dois donner des valeurs redondantes aux éléments de l'objet.

Quelqu'un connaît-il une manière décente de faire un test d'égalité par rapport à plusieurs valeurs?


Je pense que le contexte plus large doit être pris en compte pour décider de quelque chose comme ça, car il est important de savoir pourquoi vous faites de telles comparaisons.
Pointy

Eh bien, dans un jeu que je crée, je vérifie les codes du clavier afin de décider quelle fonction doit être appelée. Dans différents navigateurs, une clé a apparemment des codes de clé différents, d'où la nécessité de comparer avec plusieurs valeurs.
pimvdb

vérifier le test de performance pour plusieurs méthodes, l'opérateur logique gagne runkit.com/pramendra/58cad911146c1c00147f8d8d
Pramendra Gupta

Réponses:


175

Vous pouvez utiliser un tableau et indexOf:

if ([1,3,12].indexOf(foo) > -1)

1
J'aime celui la. Il serait même possible de créer une fonction «contient» grâce à un prototype, je suppose, afin d'éliminer l'utilisation de> -1.
pimvdb

1
@pimvdb: Notez que vous aurez peut-être besoin de votre propre implémentation car elle indexOfn'est disponible que depuis la 5ème édition d'ECMAScript.
Gumbo

Dans une autre réponse, cela est également mentionné, je ne le savais pas, merci
pimvdb

Est-ce que '>' est mieux que '! =='?
Max Waterman

117

Dans ECMA2016, vous pouvez utiliser la méthode includes . C'est la manière la plus propre que j'ai vue. (Pris en charge par tous les principaux navigateurs, sauf IE (Polyfill est dans le lien)

if([1,3,12].includes(foo)) {
    // ...
}

4
Pour les variables if([a,b,c].includes(foo))ou les chaînesif(['a','b','c'].includes(foo))
Hastig Zusammenstellen

pour les variables => if([a,b,c].includes(foo))car le guillemet le rendra chaîne. @HastigZusammenstellen
iambinodstha

2
@BinodShrestha Je pense que c'est ce que j'ai écrit, sauf si je manque quelque chose. "Pour les variables .. ou les chaînes ..."
Hastig Zusammenstellen

16

En utilisant les réponses fournies, je me suis retrouvé avec ce qui suit:

Object.prototype.in = function() {
    for(var i=0; i<arguments.length; i++)
       if(arguments[i] == this) return true;
    return false;
}

Il peut être appelé comme:

if(foo.in(1, 3, 12)) {
    // ...
}

Edit: Je suis tombé sur cette «astuce» récemment qui est utile si les valeurs sont des chaînes et ne contiennent pas de caractères spéciaux. Pour les caractères spéciaux, cela devient moche en raison de l'échappement et est également plus sujet aux erreurs à cause de cela.

/foo|bar|something/.test(str);

Pour être plus précis, cela vérifiera la chaîne exacte, mais là encore, c'est plus compliqué pour un simple test d'égalité:

/^(foo|bar|something)$/.test(str);

5
La bonté! Je suis étonné que cela fonctionne - inest un mot-clé en Javascript. Par exemple, l'exécution function in() {}; in()entraîne une erreur de syntaxe, du moins dans Firefox, et je ne sais pas pourquoi votre code ne le fait pas. :-)
Ben Blank

3
En outre, il est souvent considéré comme une mauvaise pratique d'étendre Object.prototype, car cela affecte for (foo in bar)de manière malheureuse. Peut-être le changer function contains(obj) { for (var i = 1; i < arguments.length; i++) if (arguments[i] === obj) return true; return false; }et passer l'objet comme argument?
Ben Blank le

Merci pour votre réaction, et le compilateur de fermeture Google renvoie également une erreur lors de la compilation alors que ce code lui-même fonctionne à merveille. Quoi qu'il en soit, il a été suggéré qu'une classe d'assistance peut mieux faire ce travail que d'étendre Object.prototypecomme vous le mentionnez également. Cependant, je préfère cette façon, car la notation pour la vérification est belle, juste foo.in(1, 2, "test", Infinity), facile comme bonjour.
pimvdb

Et définir une fonction de in()la manière habituelle échoue également sur Chrome, mais le prototype fonctionne ... :)
pimvdb

3
L'extension du Object.prototype est un anti-pattern et ne doit pas être utilisé. Utilisez plutôt une fonction.
Vernon

9

Vous pouvez écrire if(foo in L(10,20,30))si vous définissez Lêtre

var L = function()
{
    var obj = {};
    for(var i=0; i<arguments.length; i++)
        obj[arguments[i]] = null;

    return obj;
};

C'est encore plus clair que les réponses précédentes, merci!
pimvdb

2
Je pense qu'il est prudent de remarquer que les fonctions prototypes sont également `` dans '' un tableau / objet - donc s'il y a une fonction `` supprimer '' dans le prototype d'un tableau / objet, elle retournera toujours true si vous codez 'remove' in L(1, 3, 12), bien que vous ne l'ayez pas spécifiez 'remove' pour être mis dans la liste.
pimvdb

7

Ceci est facilement extensible et lisible:

switch (foo) {
    case 1:
    case 3:
    case 12:
        // ...
        break

    case 4:
    case 5:
    case 6:
        // something else
        break
}

Mais pas forcément plus facile :)


J'ai en fait utilisé cette méthode car je voulais mettre un commentaire à côté de chaque valeur pour décrire à quoi la valeur se rapportait, c'était le moyen le plus lisible pour y parvenir.
pholcroft

@pholcroft si vous avez besoin de décrire à quoi correspond chaque valeur, vous devez créer une énumération, ou une classe, et écrire vos propriétés pour avoir des noms descriptifs.
red_dorian

6
var a = [1,2,3];

if ( a.indexOf( 1 ) !== -1 ) { }

Notez que indexOf n'est pas dans le noyau ECMAScript. Vous aurez besoin d'un extrait pour IE et éventuellement d'autres navigateurs qui ne prennent pas en charge Array.prototype.indexOf.

if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(searchElement /*, fromIndex */)
  {
    "use strict";

    if (this === void 0 || this === null)
      throw new TypeError();

    var t = Object(this);
    var len = t.length >>> 0;
    if (len === 0)
      return -1;

    var n = 0;
    if (arguments.length > 0)
    {
      n = Number(arguments[1]);
      if (n !== n)
        n = 0;
      else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
        n = (n > 0 || -1) * Math.floor(Math.abs(n));
    }

    if (n >= len)
      return -1;

    var k = n >= 0
          ? n
          : Math.max(len - Math.abs(n), 0);

    for (; k < len; k++)
    {
      if (k in t && t[k] === searchElement)
        return k;
    }
    return -1;
  };
}

C'est directement la copie de Mozilla. Était trop paresseux pour lier. developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
meder omuraliev

Y a-t-il une raison pour le décalage de bits lorsque vous définissez len si vous ne déplacez pas réellement les bits? Y a-t-il une coercition implicite ici?
jaredad7

3

De plus, puisque les valeurs par rapport auxquelles vous vérifiez le résultat sont toutes uniques, vous pouvez également utiliser Set.prototype.has () .

var valid = [1, 3, 12];
var goodFoo = 3;
var badFoo = 55;

// Test
console.log( new Set(valid).has(goodFoo) );
console.log( new Set(valid).has(badFoo) );


2

Maintenant, vous pouvez avoir une meilleure solution pour résoudre ce scénario, mais d'une autre manière que j'ai préférée.

const arr = [1,3,12]
if( arr.includes(foo)) { // it will return true if you `foo` is one of array values else false
  // code here    
}

J'ai préféré la solution ci-dessus à la vérification d'indexOf où vous devez également vérifier l'index.

comprend des documents

if ( arr.indexOf( foo ) !== -1 ) { }

1

J'ai aimé la réponse acceptée, mais j'ai pensé que ce serait bien de lui permettre de prendre également des tableaux, alors je l'ai étendue à ceci:

Object.prototype.isin = function() {
    for(var i = arguments.length; i--;) {
        var a = arguments[i];
        if(a.constructor === Array) {
            for(var j = a.length; j--;)
                if(a[j] == this) return true;
        }
        else if(a == this) return true;
    }
    return false;
}

var lucky = 7,
    more = [7, 11, 42];
lucky.isin(2, 3, 5, 8, more) //true

Vous pouvez supprimer la contrainte de type en changeant ==à ===.


1

Si vous avez accès à Underscore, vous pouvez utiliser les éléments suivants:

if (_.contains([1, 3, 12], foo)) {
  // ...
}

contains autrefois fonctionnait dans Lodash (avant la V4), vous devez maintenant utiliser includes

if (_.includes([1, 3, 12], foo)) {
  handleYellowFruit();
}

1

Ceci est une petite fonction d'aide arror:

const letters = ['A', 'B', 'C', 'D'];

function checkInList(arr, val) {
  return arr.some(arrVal => val === arrVal);
}

checkInList(letters, 'E');   // false
checkInList(letters, 'A');   // true

Plus d'infos ici...


Très bonne approche: à partir de votre idée, vous pouvez également le faire sans fonction d'assistance:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].some(option => option === valToCheck); // false
Giorgio Tempesta

Mais une fois que j'ai commencé à peaufiner le code, j'ai trouvé que cela includesdevenait la meilleure option, comme dans la réponse de @ alister:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].includes(valToCheck); // false
Giorgio Tempesta

0

J'ai simplement utilisé la fonction jQuery inArray et un tableau de valeurs pour accomplir cela:

myArr = ["A", "B", "C", "C-"];

var test = $.inArray("C", myArr)  
// returns index of 2 returns -1 if not in array

if(test >= 0) // true

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.