Déterminer si la chaîne est dans la liste en JavaScript

263

En SQL, nous pouvons voir si une chaîne est dans une liste comme ceci:

Column IN ('a', 'b', 'c')

Quelle est la bonne façon de procéder en JavaScript? C'est tellement maladroit de faire ça:

if (expression1 || expression2 || str === 'a' || str === 'b' || str === 'c') {
   // do something
}

Et je ne suis pas sûr de la performance ou de la clarté de ceci:

if (expression1 || expression2 || {a:1, b:1, c:1}[str]) {
   // do something
}

Ou on pourrait utiliser la fonction de commutation:

var str = 'a',
   flag = false;

switch (str) {
   case 'a':
   case 'b':
   case 'c':
      flag = true;
   default:
}

if (expression1 || expression2 || flag) {
   // do something
}

Mais c'est un désordre horrible. Des idées?

Dans ce cas, je dois utiliser Internet Explorer 7 car c'est pour une page intranet d'entreprise. Donc ['a', 'b', 'c'].indexOf(str) !== -1, ne fonctionnera pas nativement sans sucre syntaxique.

ErikE
la source
1
Pourriez-vous expliquer quelle est exactement la différence entre "la chaîne est dans la liste" et "le tableau inclut un objet"?
Michał Perłakowski
2
@Gothdo Parce qu'une liste n'est pas toujours un tableau et qu'une chaîne n'est pas un objet? Comment pourrait-il être plus clair?
ErikE
@ErikE si tel est le cas ce que vous avez mentionné dans NOTE, cette question doit être fermée, aucune autre prime / réponse ne devrait être autorisée. Les réponses déjà publiées sont suffisantes pour que n'importe qui puisse obtenir de l'aide.
Vikasdeep Singh
@VicJordan Peut-être voudriez-vous supprimer votre commentaire car il ne s'applique plus.
ErikE

Réponses:

280

Vous pouvez appeler indexOf:

if (['a', 'b', 'c'].indexOf(str) >= 0) {
    //do something
}
SLaks
la source
15
Le seul problème Array.prototype.indexOfest qu'il ne fonctionnera pas sur IE, malheureusement même IE8 n'a pas cette méthode.
CMS
2
Mais vous pouvez le définir si vous le souhaitez. Voir soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer
Jason Hall
8
@ImJasonH: Le code sur cette page est vraiment très mauvais à mon humble avis, par exemple, la vérification s'il Array.indexOfexiste avant de remplacer Array.prototype.indexOfce qui n'est pas la même chose. Je recommanderais l'implémentation de Mozilla disponible ici: developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/…
CMS
1
Écoutez @CMS, @Emtucifor, l'implémentation de Mozilla est bien meilleure.
Jason Hall
231

EcmaScript 6

Si vous utilisez ES6, vous pouvez construire un tableau des éléments et utiliser includes:

['a', 'b', 'c'].includes('b')

Cela a des avantages inhérents plus indexOfcar il peut bien tester la présence de NaNla liste, et peut correspondre à des éléments de tableau manquants tels que celui du milieu dans [1, , 2]la undefined. includesfonctionne également sur des tableaux de type JavaScript tels que Uint8Array.

Si vous êtes préoccupé par la prise en charge du navigateur (comme pour IE ou Edge), vous pouvez vérifier Array.includessur CanIUse.Com , et si vous souhaitez cibler un navigateur ou une version de navigateur manquante includes, je recommande polyfill.io pour le polyfilling.

Sans tableau

Vous pouvez ajouter une nouvelle isInListpropriété aux chaînes comme suit:

if (!String.prototype.isInList) {
   String.prototype.isInList = function() {
      let value = this.valueOf();
      for (let i = 0, l = arguments.length; i < l; i += 1) {
         if (arguments[i] === value) return true;
      }
      return false;
   }
}

Ensuite, utilisez-le comme ceci:

'fox'.isInList('weasel', 'fox', 'stoat') // true
'fox'.isInList('weasel', 'stoat') // false

Vous pouvez faire la même chose pour Number.prototype.

Array.indexOf

Si vous utilisez un navigateur moderne, cela indexOffonctionne toujours. Cependant, pour IE8 et versions antérieures, vous aurez besoin d'un polyfill.

Si indexOfrenvoie -1, l'élément n'est pas dans la liste. Gardez cependant à l'esprit que cette méthode ne vérifiera pas correctement NaNet qu'elle peut correspondre à un undefinedélément explicite , elle ne peut pas correspondre à un élément manquant undefinedcomme dans le tableau [1, , 2].

Polyfill pour indexOfou includesdans IE, ou tout autre navigateur / version sans support

Si vous ne souhaitez pas utiliser un service comme polyfill.io comme mentionné ci-dessus, vous pouvez toujours inclure dans vos propres polyfills personnalisés conformes aux normes du code source. Par exemple, Mozilla Developer Network en a un pour indexOf.

Dans cette situation où j'ai dû faire une solution pour Internet Explorer 7, j'ai "roulé ma propre" version plus simple de la indexOf()fonction qui n'est pas conforme aux normes:

if (!Array.prototype.indexOf) {
   Array.prototype.indexOf = function(item) {
      var i = this.length;
      while (i--) {
         if (this[i] === item) return i;
      }
      return -1;
   }
}

Cependant, je ne pense pas que la modification Array.prototypesoit la meilleure réponse à long terme. La modification Objectet les Arrayprototypes en JavaScript peuvent entraîner de graves bugs. Vous devez décider si cela est sûr dans votre propre environnement. Il est important de noter que l'itération d'un tableau (lorsque Array.prototype a ajouté des propriétés) avec for ... inrenverra le nouveau nom de la fonction comme l'une des clés:

Array.prototype.blah = function() { console.log('blah'); };
let arr = [1, 2, 3];
for (let x in arr) { console.log(x); }
// Result:
0
1
2
blah // Extra member iterated over!

Votre code peut fonctionner maintenant, mais au moment où quelqu'un dans le futur ajoutera une bibliothèque ou un plugin JavaScript tiers qui ne protège pas avec zèle contre les clés héritées, tout peut casser.

L'ancienne façon d'éviter cette rupture est, pendant l'énumération, de vérifier chaque valeur pour voir si l'objet l'a réellement en tant que propriété non héritée avec if (arr.hasOwnProperty(x))et seulement ensuite de travailler avec x.

La nouvelle façon de ES6 pour éviter ce problème extra-clé est d'utiliser au oflieu de in, for (let x of arr). Cependant, à moins que vous ne puissiez garantir que tout votre code et vos bibliothèques tierces respectent strictement cette méthode, alors aux fins de cette question, vous voudrez probablement simplement utiliser includescomme indiqué ci-dessus.

ErikE
la source
2
voici une autre bonne Polyfill pour indexOffourni par MDN. Il fait essentiellement la même chose mais avec quelques courts-circuits pour une évaluation facile.
KyleMit
1
Downvoter: veuillez commenter. Quel est le problème avec ça? Cette fonction n'est PAS conforme aux normes et n'essaye pas de l'être.
ErikE
Vous devriez utiliser quelque chose déjà testé comme le polyfill noté par le lien @KyleMit: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
lmiguelmh
@lmiguelmh Que diriez-vous du "polyfill" auquel j'ai moi-même posté un lien, à partir des documents Mozilla eux-mêmes? En tout cas, cette fonction est tellement simple que je ne suis vraiment pas trop préoccupé par les tests. Et quiconque l'est, ne devrait pas prendre une fonction "déjà testée" mais devrait tester lui-même tout ce qu'il utilise. Votre commentaire est donc un peu déplacé. Avez-vous identifié un défaut spécifique avec ma fonction?
ErikE
@ErikE vous avez raison, cette fonction est morte et toute personne utilisant votre réponse doit le savoir. Je n'ai pas vu votre lien en premier lieu parce que je cherchais des "réponses"
lmiguelmh
53

La plupart des réponses suggèrent la Array.prototype.indexOfméthode, le seul problème est qu'elle ne fonctionnera sur aucune version d'IE avant IE9.

Comme alternative, je vous laisse deux autres options qui fonctionneront sur tous les navigateurs:

if (/Foo|Bar|Baz/.test(str)) {
  // ...
}


if (str.match("Foo|Bar|Baz")) {
  // ...
}
CMS
la source
Hmmm, merci d'avoir mentionné ça, CMS. Il se trouve que c'est pour un intranet d'entreprise et qu'ils utilisent ... devinez quoi ... IE. Étant donné que la méthode d'expression régulière me donne la volonté, je devrai soit créer une fonction qui boucle, soit utiliser la méthode objet que j'ai suggérée dans mon article (troisième bloc de code).
ErikE
13
Cela correspondra "HiFooThere"- j'irais avec à la /^(?:Foo|Bar|Baz)$/place (début de chaîne, groupe non capturant, fin de chaîne).
TrueWill
indexOfest désormais pris en charge dans IE 9 et supérieur selon MDN .
krock
28

Les tableaux ont une indexOfméthode qui peut être utilisée pour rechercher des chaînes:

js> a = ['foo', 'bar', 'baz']
foo,bar,baz
js> a.indexOf('bar')
1
js> a.indexOf('quux')
-1
harto
la source
3
Cela échouera sur les anciens navigateurs.
epascarello
Oups ... mentionner que cela ne fonctionne pas dans IE aurait été bien. :)
ErikE
2
@epascarello: Non seulement dans les navigateurs plus anciens, il échouera sur n'importe quel IE, même dans IE8 :(
CMS
12

Un truc que j'ai utilisé est

>>> ("something" in {"a string":"", "somthing":"", "another string":""})
false
>>> ("something" in {"a string":"", "something":"", "another string":""})
true

Vous pourriez faire quelque chose comme

>>> a = ["a string", "something", "another string"];
>>> b = {};
>>> for(var i=0; i<a.length;i++){b[a[i]]="";} /* Transform the array in a dict */
>>> ("something" in b)
true
Esteban Küber
la source
voyager, utilise-t-il "in" plus rapidement / plus lentement / mieux / pire que simplement essayer de déréférencer l'article comme mon troisième bloc de code dans ma question l'a montré? De plus, si vous allez parcourir la chose en boucle, je pense que vous pouvez aussi bien vérifier si l'élément est dans le tableau à ce moment-là ... juste envelopper la boucle dans une fonction. Et pour ce que ça vaut, var i=a.length;while (i--) {/*use a[i]*/}c'est la méthode de boucle la plus rapide (si l'ordre inverse est acceptable).
ErikE
@Emtucifor: cela dépend vraiment de ce que vous faites, et je suppose que cela pourrait fonctionner différemment sur différents moteurs javascript. Si vos données nécessitent à tout moment l'utilisation d'un dictionnaire, il est préférable de les créer de cette façon. Je pense que ce sera plus rapide en raison des détails d'implémentation sur les moteurs (l'utilisation de tables de hachage pour l'objet) une fois que l'objet dict est créé .
Esteban Küber
En JavaScript, cela ne s'appelle pas un dict, cela s'appelle un objet .
Solomon Ucko
8

Voici la mienne:

String.prototype.inList=function(list){
    return (Array.apply(null, arguments).indexOf(this.toString()) != -1)
}

var x = 'abc';
if (x.inList('aaa','bbb','abc'))
    console.log('yes');
else
    console.log('no');

Celui-ci est plus rapide si vous êtes d'accord pour passer un tableau:

String.prototype.inList=function(list){
    return (list.indexOf(this.toString()) != -1)
}

var x = 'abc';
if (x.inList(['aaa','bbb','abc']))
    console.log('yes')

Voici le jsperf: http://jsperf.com/bmcgin-inlsit

Brian McGinity
la source
Franchement, celui où vous passez un tableau serait probablement plus utile, et il devrait probablement être sur Arrayle prototype de: peut-être quelque chose comme Array.prototype.contains.
Solomon Ucko
6

RegExpest universel, mais je comprends que vous travaillez avec des tableaux. Alors, découvrez cette approche. Je l'utilise pour l'utiliser, et c'est très efficace et très rapide!

var str = 'some string with a';
var list = ['a', 'b', 'c'];
var rx = new RegExp(list.join('|'));

rx.test(str);

Vous pouvez également appliquer certaines modifications, à savoir:

Bon mot

new RegExp(list.join('|')).test(str);

Insensible à la casse

var rx = new RegExp(list.join('|').concat('/i'));


Et plein d'autres!

sospedra
la source
L'utilisation de regex nécessite d'éviter un tas de caractères spéciaux. C'est aussi moins clair. Je ne pense pas que ce soit une bonne solution.
ErikE
@ErikE Je comprends vos réservations, j'ai donc mis à jour ma réponse en ajoutant un autre code qui n'utilise pas RegExp, il est rétrocompatible avec IE, très idiomatique et rapide.
sospedra
Votre code supplémentaire est un quasi-double de ma réponse que j'ai fournie 5 ans avant que vous décidiez de publier une solution Regex. Merci d'avoir participé et en même temps, je pense que revenir à votre réponse précédente est le meilleur.
ErikE
@ErikE oui, vous avez raison, je n'utilise pas pour vérifier les réponses à la question. Et je suis d'accord, c'est vraiment similaire.
sospedra
6

Utiliser indexOf (cela ne fonctionne pas avec IE8).

if (['apple', 'cherry', 'orange', 'banana'].indexOf(value) >= 0) {
    // found
}

Pour prendre en charge IE8, vous pouvez implémenter l'indexOf de Mozilla.

if (!Array.prototype.indexOf) {
    // indexOf polyfill code here
}

Expressions régulières via String.prototype.match (docs).

if (fruit.match(/^(banana|lemon|mango|pineapple)$/)) {

}
Tiago Medici
la source
1
Avez-vous remarqué que vous dupliquez exactement d'autres réponses sur la page? Cela n'ajoute aucune valeur.
ErikE
5

On dirait que vous devez utiliser la fonction in_array.

jQuery -> inArray

Prototype -> Array.indexOf

Ou, voyez ces exemples si vous n'utilisez pas jQuery ou Prototype:

Note stylistique: les variables nommées thisthing thatthing, devraient être nommées pour vous dire quelque chose sur ce qu'elles contiennent (nom).

LG_PDX
la source
Oh, ce n'étaient pas des variables mais étaient conçues comme des espaces réservés aléatoires pour les expressions ... juste un exemple de la façon dont je prévoyais d'utiliser le script.
ErikE
5

En plus de indexOf(ce que d'autres affiches ont suggéré), l'utilisation de Enumerable.include () du prototype peut rendre cela plus net et concis:

var list = ['a', 'b', 'c'];
if (list.include(str)) {
  // do stuff
}
pkaeding
la source
5
Enumerable.include () a été déconseillé, mais Array.prototype.includes () arrive, et fonctionne déjà dans la plupart des navigateurs sauf IE (bien sûr) et Edge (bien sûr).
whitebeard
2

Merci pour la question et la solution utilisant la méthode Array.indexOf.

J'ai utilisé le code de cette solution pour créer une fonction inList () qui, à l'OMI, rendrait l'écriture plus simple et la lecture plus claire:

function inList(psString, psList) 
{
    var laList = psList.split(',');

    var i = laList.length;
    while (i--) {
        if (laList[i] === psString) return true;
    }
    return false;
}

USAGE:

if (inList('Houston', 'LA,New York,Houston') {
  // THEN do something when your string is in the list
}
JMichaelTX
la source
1
Les littéraux de tableau Javascript sont si faciles, je ne vois pas pourquoi vous vous sépareriez quand vous pourriez le faire 'Houston'.inList(['LA', 'New York', 'Houston']). Peut if (!String.prototype.inList) {String.prototype.inList = function(arr) {return arr.indexOf(this) >= 0};}- être ou en utilisant votre whileméthode.
ErikE
0

Je suis surpris que personne n'ait mentionné une fonction simple qui prend une chaîne et une liste.

function in_list(needle, hay)
{
    var i, len;

    for (i = 0, len = hay.length; i < len; i++)
    {
        if (hay[i] == needle) { return true; }
    }

    return false;
}

var alist = ["test"];

console.log(in_list("test", alist));
Samuel Parkinson
la source
Jim a fait exactement ce que vous proposez , Sam, le 27 août 11 à 1:29. En fait, ma réponse sélectionnée est à peu près la même chose, fournissant simplement la chaîne avec cela plutôt qu'un paramètre.
ErikE
@ErikE Désolé, la réponse de Jims m'a semblé étrange comme vous l'avez dit. Et votre réponse acceptée renvoie un int, où comme certaines personnes pourraient rencontrer cette question à la recherche d'un boolretour. Je pensais que cela pourrait aider quelques personnes.
Samuel Parkinson
0

Ma solution se traduit par une syntaxe comme celle-ci:

// Checking to see if var 'column' is in array ['a', 'b', 'c']

if (column.isAmong(['a', 'b', 'c']) {
  // Do something
}

Et je l'implémente en étendant le prototype d'objet de base, comme ceci:

Object.prototype.isAmong = function (MyArray){
   for (var a=0; a<MyArray.length; a++) {
      if (this === MyArray[a]) { 
          return true;
      }
   }
   return false;
}

Nous pourrions également nommer la méthode isInArray (mais probablement pas inArray) ou simplement isIn.

Avantages: Simple, direct et auto-documenté.

John Hicks
la source
Il pourrait y avoir des problèmes d'extension de l'objet. bolinfest.com/javascript/inheritance.php sous "L'équipe Google Maps l'a appris à la dure" et incompatibilité avec la mise en œuvre du navigateur ou le code d'un autre utilisateur. Je pense toujours que la réponse d'ErikE est la meilleure car itérer sur un tableau est plus lent que de trouver une clé dans une table de hachage une fois que la table de hachage est créée: myValues[key];où mesValeurs est un objet et la clé est n'importe quelle chaîne ou nombre.
HMR
-1

Une version simplifiée de la réponse de SLaks fonctionne également:

if ('abcdefghij'.indexOf(str) >= 0) {
    // Do something
}

.... puisque les chaînes sont en quelque sorte des tableaux eux-mêmes. :)

Si nécessaire, implémentez la fonction indexof pour Internet Explorer comme décrit précédemment.

Angezerus
la source
1
Cela ne fonctionnera qu'avec des chaînes d'une seule lettre, ce qui n'était PAS prévu.
ErikE