Determina se la stringa è presente nell'elenco in JavaScript


263

In SQL possiamo vedere se una stringa è in un elenco in questo modo:

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

Qual è un buon modo per farlo in JavaScript? È così goffo fare questo:

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

E non sono sicuro delle prestazioni o della chiarezza di questo:

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

Oppure si potrebbe usare la funzione switch:

var str = 'a',
   flag = false;

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

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

Ma è un casino orribile. Qualche idea?

In questo caso, devo usare Internet Explorer 7 per una pagina Intranet aziendale. Quindi ['a', 'b', 'c'].indexOf(str) !== -1non funzionerà in modo nativo senza zucchero di sintassi.


1
Potresti spiegare qual è esattamente la differenza tra "string is in list" e "array include a object"?
Michał Perłakowski il

2
@ Gothdo Perché un elenco non è sempre un array e una stringa non è un oggetto? Come potrebbe essere più chiaro?
ErikE,

@ErikE se questo è il caso che hai menzionato nella NOTA, allora questa domanda dovrebbe essere chiusa non ci dovrebbero essere ulteriori ricompense / risposte consentite. Le risposte già pubblicate sono sufficienti per chiunque per ottenere aiuto.
Vikasdeep Singh,

@VicJordan Forse desideri eliminare il tuo commento in quanto non è più applicabile.
ErikE,

Risposte:


280

Puoi chiamare indexOf:

if (['a', 'b', 'c'].indexOf(str) >= 0) {
    //do something
}

15
L'unico problema Array.prototype.indexOfè che non funzionerà su IE, purtroppo anche IE8 non ha questo metodo.
CMS



8
@ImJasonH: Il codice in quella pagina è davvero un errore IMHO, ad esempio, il controllo se Array.indexOfesiste prima di sovrascrivere Array.prototype.indexOfche non sono la stessa cosa. Consiglierei l'implementazione resa disponibile da Mozilla qui: developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/…
CMS

1
Ascolta @CMS, @Emtucifor, l'implementazione di Mozilla è molto meglio.
Jason Hall

231

EcmaScript 6

Se stai usando ES6, puoi costruire un array di elementi e usare includes:

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

Ciò ha alcuni vantaggi intrinseci indexOfperché può testare correttamente la presenza di NaNnell'elenco e può abbinare elementi di array mancanti come quello centrale [1, , 2]a undefined. includesfunziona anche su matrici tipizzate JavaScript come Uint8Array.

Se sei preoccupato per il supporto del browser (come per IE o Edge), puoi controllare Array.includessu CanIUse.Com e se desideri scegliere come target un browser o una versione del browser mancante includes, ti consiglio polyfill.io per il polyfilling.

Senza matrice

È possibile aggiungere una nuova isInListproprietà alle stringhe come segue:

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

Quindi usalo così:

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

Puoi fare la stessa cosa per Number.prototype.

Array.indexOf

Se stai utilizzando un browser moderno, indexOffunziona sempre. Tuttavia, per IE8 e versioni precedenti è necessario un polyfill.

Se indexOfrestituisce -1, l'elemento non è nell'elenco. Tuttavia, tieni presente che questo metodo non controlla correttamente NaNe, sebbene possa corrispondere a un esplicito undefined, non può corrispondere a un elemento mancante undefinedcome nell'array [1, , 2].

Polyfill per indexOfo includesin IE o qualsiasi altro browser / versione privo di supporto

Se non si desidera utilizzare un servizio come polyfill.io come menzionato sopra, è sempre possibile includere i propri polyfill personalizzati conformi agli standard del codice sorgente. Ad esempio, Mozilla Developer Network ne ha uno per indexOf.

In questa situazione in cui dovevo creare una soluzione per Internet Explorer 7, ho "lanciato la mia" versione più semplice della indexOf()funzione che non è conforme agli standard:

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

Tuttavia, non penso che la modifica Array.prototypesia la migliore risposta a lungo termine. La modifica Objecte i Arrayprototipi in JavaScript possono portare a gravi bug. Devi decidere se farlo è sicuro nel tuo ambiente. La nota principale è che iterando un array (quando Array.prototype ha aggiunto proprietà) for ... inrestituirà il nuovo nome della funzione come uno dei tasti:

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!

Il tuo codice potrebbe funzionare ora, ma nel momento in cui qualcuno in futuro aggiungerà una libreria o plugin JavaScript di terze parti che non protegge con zelo dalle chiavi ereditate, tutto può rompersi.

Il vecchio modo per evitare tale rottura è, durante l'enumerazione, verificare ogni valore per vedere se l'oggetto ha effettivamente come proprietà non ereditata if (arr.hasOwnProperty(x))e solo allora lavorare con quello x.

Il nuovo modo ES6 per evitare questo problema extra-chiave è di usare ofal posto di in, for (let x of arr). Tuttavia, a meno che tu non possa garantire che tutto il tuo codice e le librerie di terze parti si attengano rigorosamente a questo metodo, quindi ai fini di questa domanda probabilmente vorrai semplicemente usare includescome sopra indicato.


2
ecco un altro buon PolyFill per indexOffornito da MDN. Fondamentalmente fa la stessa cosa ma con un paio di cortocircuiti per una facile valutazione.
KyleMit,

1
Downvoter: per favore, commenta. Qual è il problema con questo? Questa funzione NON è conforme agli standard e non sta cercando di esserlo.
ErikE,

Dovresti usare qualcosa di già testato come il polyfill annotato dal link @KyleMit: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
lmiguelmh

@lmiguelmh Che ne dici del "polyfill" a cui io stesso ho pubblicato un link, dagli stessi documenti di Mozilla? In ogni caso, questa funzione è così semplice che non sono davvero troppo preoccupato per i test. E chiunque lo sia, non dovrebbe assumere una funzione "già testata", ma dovrebbe testare qualunque cosa utilizzi, da sola. Quindi il tuo commento è un po 'fuori luogo. Hai identificato un difetto specifico con la mia funzione?
ErikE

@ErikE hai ragione, questa funzione è disattivata e chiunque usi la tua risposta dovrebbe saperlo. In primo luogo non ho visto il tuo link perché stavo cercando "risposte"
lmiguelmh

53

La maggior parte delle risposte suggerisce il Array.prototype.indexOfmetodo, l'unico problema è che non funzionerà su nessuna versione di IE prima di IE9.

In alternativa, ti lascio altre due opzioni che funzioneranno su tutti i browser:

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


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

Hmmm, grazie per averlo menzionato, CMS. Accade solo che questo è per una intranet aziendale e usano ... indovina un po '... IE. Dal momento che il metodo dell'espressione regolare mi dà i willies, dovrò fare una funzione che gira o usare il metodo oggetto che ho suggerito nel mio post (terzo blocco di codice).
ErikE

13
Questo corrisponderà "HiFooThere": preferirei /^(?:Foo|Bar|Baz)$/invece (inizio della stringa, gruppo non acquisito, fine della stringa).
TrueWill,

indexOfè ora supportato in IE 9 e versioni successive in base a MDN .
Krock,

28

Le matrici hanno un indexOfmetodo che può essere usato per cercare stringhe:

js> a = ['foo', 'bar', 'baz']
foo,bar,baz
js> a.indexOf('bar')
1
js> a.indexOf('quux')
-1

3
Questo fallirà sui browser più vecchi.
epascarello,

Oops ... menzionare che questo non funziona in IE sarebbe stato bello. :)
ErikE

2
@epascarello: non solo nei browser più vecchi, fallirà su qualsiasi IE, anche in IE8 :(
CMS

12

Un trucco che ho usato è

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

Potresti fare qualcosa del genere

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

voyager, sta usando "in" un modo più veloce / più lento / migliore / peggiore del semplice tentativo di dereferenziare l'oggetto come ha mostrato il mio terzo blocco di codice nella mia domanda? Inoltre, se hai intenzione di passare in rassegna la cosa, immagino che potresti anche controllare se l'elemento è nell'array in quel momento ... basta avvolgere il ciclo in una funzione. E per quello che vale var i=a.length;while (i--) {/*use a[i]*/}è il metodo di ciclo più veloce (se l'ordine inverso è accettabile).
ErikE

@Emtucifor: dipende davvero da cosa stai facendo, e immagino che potrebbe funzionare in modo diverso su diversi motori javascript. Se i tuoi dati avrebbero bisogno in qualsiasi momento dell'uso di un dizionario, è meglio crearli in questo modo. Penserei che questo sarà più veloce a causa dei dettagli di implementazione sui motori (l'uso delle tabelle hash per l'oggetto) una volta creato l'oggetto dict .
Esteban Küber,

In JavaScript, non si chiama dict, si chiama un oggetto .
Solomon Ucko,

8

Ecco il mio:

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');

Questo è più veloce se stai bene passando un array:

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

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

Ecco il jsperf: http://jsperf.com/bmcgin-inlsit


Francamente, quello in cui passi un array sarebbe probabilmente più utile, e probabilmente dovrebbe essere sul Arrayprototipo: forse qualcosa del genere Array.prototype.contains.
Solomon Ucko

6

RegExpè universale, ma capisco che stai lavorando con le matrici. Quindi, controlla questo approccio. Lo uso per usarlo, è molto efficace e velocissimo!

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

rx.test(str);

Puoi anche applicare alcune modifiche, ad esempio:

One-liner

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

Insensibile alle maiuscole

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


E molti altri!


L'uso di regex richiede di evitare un mucchio di caratteri speciali. Inoltre è meno chiaro. Non penso sia una buona soluzione.
ErikE

@ErikE Capisco le tue prenotazioni, quindi ho aggiornato la mia risposta aggiungendo un altro codice che non utilizza RegExp, è retrocompatibile con IE, molto idiomatico e veloce.
sospedra,

Il tuo codice aggiuntivo è quasi un duplicato della mia risposta che ho fornito 5 anni prima che decidessi di pubblicare una soluzione Regex. Grazie per aver partecipato e allo stesso tempo penso che tornare alla tua risposta precedente sia la cosa migliore.
ErikE,

@ErikE sì, hai ragione, non uso per controllare le risposte alla domanda. E sono d'accordo che è davvero simile.
sospedra,

6

Utilizzando indexOf (non funziona con IE8).

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

Per supportare IE8, è possibile implementare l'indiceOf di Mozilla.

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

Espressioni regolari tramite String.prototype.match (docs).

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

}

1
Noti che stai esattamente duplicando altre risposte sulla pagina? Questo non aggiunge alcun valore.
ErikE,

5

Sembra che tu debba usare la funzione in_array.

jQuery -> inArray

Prototype -> Array.indexOf

Oppure, vedi questi esempi se non stai usando jQuery o Prototype:

Nota stilistica: le variabili denominate thisthing thatthing, dovrebbero essere nominate per dirti qualcosa su ciò che contengono (sostantivo).


Oh, non erano variabili ma erano intese come segnaposti casuali per le espressioni ... solo un esempio di come avevo pianificato di usare la sceneggiatura.
ErikE

5

Oltre a indexOf(che altri poster hanno suggerito), l'uso del prototipo Enumerable.include () può rendere questo più pulito e conciso:

var list = ['a', 'b', 'c'];
if (list.include(str)) {
  // do stuff
}

5
Enumerable.include () è stato deprecato, ma Array.prototype.includes () è in arrivo e funziona già nella maggior parte dei browser tranne IE (ovviamente) e Edge (ovviamente).
barba bianca il

2

Grazie per la domanda e la soluzione che utilizza il metodo Array.indexOf.

Ho usato il codice di questa soluzione per creare una funzione inList () che, IMO, avrebbe reso la scrittura più semplice e la lettura più chiara:

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

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

USO:

if (inList('Houston', 'LA,New York,Houston') {
  // THEN do something when your string is in the list
}

1
I letterali di array Javascript sono così facili, non vedo perché ti dividerei quando potresti farlo 'Houston'.inList(['LA', 'New York', 'Houston']). Forse if (!String.prototype.inList) {String.prototype.inList = function(arr) {return arr.indexOf(this) >= 0};}o usando il tuo whilemetodo.
ErikE,

0

Sono sorpreso che nessuno abbia menzionato una semplice funzione che accetta una stringa e un elenco.

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

Jim ha fatto esattamente quello che mi hai suggerito , Sam, il 27 agosto 11 alle 1:29. In effetti, la mia risposta selezionata è praticamente la stessa cosa, semplicemente fornendo alla stringa questo piuttosto che un parametro.
ErikE,

@ErikE Mi dispiace, la risposta di Jims mi è sembrata strana come hai detto. E la tua risposta accettata restituisce un int, dove alcune persone potrebbero imbattersi in questa domanda in cerca di un boolritorno. Ho pensato che potesse aiutare alcune persone.
Samuel Parkinson,

0

La mia soluzione si traduce in una sintassi come questa:

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

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

E lo implemento estendendo il prototipo di oggetto di base, in questo modo:

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

In alternativa, potremmo nominare il metodo isInArray (ma probabilmente non inArray) o semplicemente isIn.

Vantaggi: semplice, diretto e auto-documentante.


Potrebbe essersi verificato un problema con l'estensione dell'oggetto. bolinfest.com/javascript/inheritance.php sotto "Il team di Google Maps lo ha imparato nel modo più duro" e incompatibilità con l'implementazione del browser o il codice di altri utenti. Penso ancora che la risposta di ErikE sia la migliore poiché l'iterazione su un array è più lenta della ricerca di una chiave in una hashmap una volta creata la hashmap: myValues[key];dove myValues ​​è un oggetto e la chiave è qualsiasi stringa o numero.
HMR,

-1

Una versione semplificata della risposta di SLaks funziona anche:

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

.... poiché le stringhe sono una sorta di array stessi. :)

Se necessario, implementare la funzione indexof per Internet Explorer come descritto prima di me.


1
Funzionerà solo con stringhe di una sola lettera, che NON era previsto.
ErikE,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.