Sprawdź, czy ciąg jest na liście w JavaScript


262

W SQL możemy sprawdzić, czy ciąg znajduje się na liście w taki sposób:

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

Jaki jest dobry sposób na to w JavaScript? To takie niezręczne:

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

Nie jestem pewien co do wydajności ani przejrzystości tego:

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

Lub można użyć funkcji przełączania:

var str = 'a',
   flag = false;

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

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

Ale to okropny bałagan. Jakieś pomysły?

W takim przypadku muszę użyć programu Internet Explorer 7, ponieważ jest on przeznaczony dla firmowej strony intranetowej. Więc ['a', 'b', 'c'].indexOf(str) !== -1nie będzie działać natywnie bez cukru składniowego.


1
Czy możesz wyjaśnić, jaka dokładnie jest różnica między „ciąg znaków na liście” a „tablica zawiera obiekt”?
Michał Perłakowski

2
@Gothdo Ponieważ lista nie zawsze jest tablicą, a łańcuch nie jest obiektem? Jak to może być jaśniejsze?
ErikE

@ErikE, jeśli tak jest w przypadku, o którym wspomniałeś w UWAGA, to pytanie powinno zostać zamknięte, nie powinno być żadnych dalszych nagród / odpowiedzi. Już opublikowane odpowiedzi są wystarczające, aby każdy mógł uzyskać pomoc.
Vikasdeep Singh

@VicJordan Być może chcesz usunąć swój komentarz, ponieważ nie ma on już zastosowania.
ErikE

Odpowiedzi:


279

Możesz zadzwonić indexOf:

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

15
Jedynym problemem Array.prototype.indexOfjest to, że nie będzie działać na IE, niestety nawet IE8 nie ma tej metody.
CMS

2
Ale możesz to zdefiniować, jeśli chcesz. Zobacz soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer
Jason Hall


8
@ImJasonH: Kod na tej stronie jest naprawdę zły IMHO, na przykład sprawdź, czy Array.indexOfistnieje przed zastąpieniem, Array.prototype.indexOfktóre nie są tym samym. Poleciłbym implementację udostępnioną przez Mozillę dostępną tutaj: developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/…
CMS

1
Posłuchaj @CMS, @Emtucifor, implementacja Mozilli jest znacznie lepsza.
Jason Hall

230

EcmaScript 6

Jeśli używasz ES6, możesz zbudować tablicę przedmiotów i użyć includes:

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

Ma to pewne nieodłączne zalety, indexOfponieważ może poprawnie przetestować obecność NaNna liście i może dopasować brakujące elementy tablicy, takie jak środkowy [1, , 2]do undefined. includesdziała również na tablicach typu JavaScript, takich jak Uint8Array.

Jeśli martwisz się o obsługę przeglądarki (np. IE lub Edge), możesz to sprawdzić Array.includesna CanIUse.Com , a jeśli chcesz kierować na przeglądarkę lub brakującą wersję przeglądarki includes, polecam polyfill.io do wypełniania.

Bez szyku

Możesz dodać nową isInListwłaściwość do ciągów w następujący sposób:

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

Następnie użyj go w następujący sposób:

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

Możesz zrobić to samo dla Number.prototype.

Array.indexOf

Jeśli korzystasz z nowoczesnej przeglądarki, indexOfzawsze działa. Jednak w IE8 i wcześniejszych wersjach będziesz potrzebować wielokrotnego wypełnienia.

Jeśli indexOfzwraca -1, pozycji nie ma na liście. Pamiętaj jednak, że ta metoda nie sprawdza poprawnie NaNi chociaż może dopasować jawne undefined, nie może dopasować brakującego elementu do undefinedjak w tablicy [1, , 2].

Wielokrotne wypełnianie dla indexOflub includesw IE, lub dowolnej innej przeglądarce / wersji bez wsparcia

Jeśli nie chcesz korzystać z usługi takiej jak polyfill.io, jak wspomniano powyżej, zawsze możesz zawrzeć w swoim własnym kodzie źródłowym niestandardowe polypełniacze zgodne z normami. Na przykład Mozilla Developer Network ma jeden dla indexOf.

W tej sytuacji, w której musiałem stworzyć rozwiązanie dla Internet Explorera 7, „opracowałem własną” prostszą wersję indexOf()funkcji, która nie jest zgodna ze standardami:

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

Nie sądzę jednak, aby modyfikowanie Array.prototypebyło najlepszą odpowiedzią na dłuższą metę. Modyfikowanie Objecti tworzenie Arrayprototypów w JavaScript może prowadzić do poważnych błędów. Musisz zdecydować, czy jest to bezpieczne we własnym środowisku. Najważniejszą rzeczą jest to, że iteracja tablicy (gdy Array.prototype ma właściwości) for ... inzwróci nową nazwę funkcji jako jeden z kluczy:

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!

Twój kod może już działać, ale w momencie, gdy ktoś w przyszłości doda bibliotekę JavaScript lub wtyczkę innej firmy, która nie gorliwie chroni przed odziedziczonymi kluczami, wszystko może się zepsuć.

Starym sposobem uniknięcia tego zerwania jest, podczas wyliczania, sprawdzenie każdej wartości, aby sprawdzić, czy obiekt rzeczywiście ma ją jako właściwość nieprzechowaną, if (arr.hasOwnProperty(x))i tylko z nią pracować x.

Nowy ES6 sposobem na uniknięcie tego problemu dodatkowy klucz jest użycie ofzamiast in, for (let x of arr). Jednakże, chyba że możesz zagwarantować, że cały twój kod i biblioteki stron trzecich ściśle trzymają się tej metody, to do celów tego pytania prawdopodobnie po prostu będziesz chciał użyć, includesjak podano powyżej.


2
oto kolejny dobry PolyFill dla indexOfMDN. Zasadniczo robi to samo, ale z kilkoma zwarciami dla łatwej oceny.
KyleMit

1
Downvoter: proszę o komentarz. W czym problem? Ta funkcja NIE jest zgodna ze standardami i nie próbuje być.
ErikE

Powinieneś użyć czegoś już przetestowanego, jak polifill odnotowany przez link @KyleMit: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
lmiguelmh

@lmiguelmh Co powiesz na „polifill”, do którego sam zamieściłem link, z samych dokumentów Mozilli? W każdym razie ta funkcja jest tak prosta, że ​​tak naprawdę nie martwię się zbytnio testowaniem. I każdy, kto jest, nie powinien przyjmować funkcji „już przetestowanej”, ale powinien sam przetestować wszystko, czego używa. Twój komentarz jest więc trochę niewłaściwy. Czy zidentyfikowałeś konkretną wadę w mojej funkcji?
ErikE

@ErikE masz rację, ta funkcja jest martwa i każdy, kto użyje Twojej odpowiedzi, powinien ją znać. Nie widziałem twojego linku w pierwszej kolejności, ponieważ szukałem „odpowiedzi”
lmiguelmh,

53

Większość odpowiedzi sugeruje Array.prototype.indexOfmetodę, jedynym problemem jest to, że nie będzie działać na żadnej wersji IE przed IE9.

Alternatywnie pozostawiam ci dwie dodatkowe opcje, które będą działać na wszystkich przeglądarkach:

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


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

Hmmm, dzięki, że o tym wspomniałeś, CMS. Tak się składa, że ​​jest to korporacyjny intranet i używają ... zgadnij, co ... IE. Ponieważ metoda wyrażeń regularnych daje mi testamenty, będę musiał albo utworzyć funkcję, która zapętli się, albo użyć metody obiektowej, którą zasugerowałem w swoim poście (trzeci blok kodu).
ErikE

13
To pasuje "HiFooThere"- wybrałbym /^(?:Foo|Bar|Baz)$/zamiast tego (początek łańcucha, grupa nie przechwytująca, koniec łańcucha).
TrueWill,

indexOfjest teraz obsługiwany w IE 9 i nowszych zgodnie z MDN .
krock

28

Tablice mają indexOfmetodę, za pomocą której można wyszukiwać ciągi:

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

3
Nie powiedzie się to w starszych przeglądarkach.
epascarello

Ups ... wspominając, że to nie działa w IE byłoby miło. :)
ErikE

2
@epascarello: Nie tylko w starszych przeglądarkach zawiedzie na każdym IE, nawet w IE8 :(
CMS

12

Trik, którego użyłem, to

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

Możesz zrobić coś takiego

>>> 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, czy użycie „w” jest szybsze / wolniejsze / lepsze / gorsze niż próba wyrejestrowania elementu, jak pokazał mój trzeci blok kodu w pytaniu? Ponadto, jeśli zamierzasz przejść przez pętlę, myślę, że możesz równie dobrze sprawdzić, czy element jest w tym czasie w tablicy ... po prostu zawiń pętlę w funkcji. A za to, co jest warte, var i=a.length;while (i--) {/*use a[i]*/}jest najszybsza metoda pętli (jeśli dopuszczalne jest odwrotne zamówienie).
ErikE

@Emtucifor: to naprawdę zależy od tego, co robisz, i myślę, że może działać inaczej w różnych silnikach javascript. Jeśli twoje dane w dowolnym momencie będą wymagać użycia słownika, lepiej jest utworzyć go w ten sposób. Sądzę, że będzie to szybsze ze względu na szczegóły implementacji w silnikach (użycie tabel skrótów dla obiektu) po utworzeniu obiektu dict .
Esteban Küber,

W JavaScript nie nazywa się to dict, nazywa się obiektem .
Solomon Ucko

8

To moje:

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

Ten jest szybszy, jeśli jesteś w stanie przekazać tablicę:

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

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

Oto jsperf: http://jsperf.com/bmcgin-inlsit


Szczerze mówiąc, ten, w którym przekazujesz tablicę, byłby prawdopodobnie bardziej przydatny i prawdopodobnie powinien być na Arrayprototypie: może coś w tym rodzaju Array.prototype.contains.
Solomon Ucko

6

RegExpjest uniwersalny, ale rozumiem, że pracujesz z tablicami. Sprawdź to podejście. Używam go, a to jest bardzo skuteczne i płonące szybko!

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

rx.test(str);

Możesz także zastosować pewne modyfikacje, np .:

Jednowarstwowa

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

Bez względu na wielkość liter

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


I wiele innych!


Korzystanie z wyrażeń regularnych wymaga unikania wielu znaków specjalnych. Jest to również mniej jasne. Nie sądzę, że to dobre rozwiązanie.
ErikE

@ErikE Rozumiem twoje rezerwacje, więc zaktualizowałem swoją odpowiedź dodając kolejny kod, który nie używa RegExp, jest on kompatybilny z IE, bardzo idiomatyczny i szybki.
sospedra

Twój dodatkowy kod jest prawie duplikatem mojej odpowiedzi, którą dostarczyłem 5 lat, zanim zdecydowałeś się opublikować rozwiązanie Regex. Dziękujemy za udział, a jednocześnie uważam, że najlepsze jest cofnięcie się do poprzedniej odpowiedzi.
ErikE

@ErikE tak, masz rację, nie używam do sprawdzania odpowiedzi w pytaniu. I zgadzam się, że to naprawdę podobne.
sospedra

6

Korzystanie z indexOf (nie działa z IE8).

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

Aby wesprzeć IE8, możesz zaimplementować indexOf Mozilli.

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

Wyrażenia regularne za pośrednictwem String.prototype.match (docs).

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

}

1
Czy zauważysz, że dokładnie kopiujesz inne odpowiedzi na stronie? To nie dodaje żadnej wartości.
ErikE

5

Wygląda na to, że musisz użyć funkcji in_array.

jQuery -> inArray

Prototyp -> Array.indexOf

Lub zobacz poniższe przykłady, jeśli nie używasz jQuery lub Prototype:

Uwaga stylistyczna: zmienne o nazwie thisthing thatthing, powinny być nazwane, aby powiedzieć ci coś o tym, co zawierają (rzecz.).


Och, to nie były zmienne, ale miały być losowymi symbolami zastępczymi dla wyrażeń ... tylko przykład tego, jak planowałem używać skryptu.
ErikE


2

Dzięki za pytanie i rozwiązanie za pomocą metody Array.indexOf.

Użyłem kodu z tego rozwiązania, aby utworzyć funkcję inList (), która, IMO, uprościłaby pisanie, a odczyt był jaśniejszy:

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

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

STOSOWANIE:

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

1
Dosłowne tablice JavaScript są tak łatwe, że nie rozumiem, dlaczego podzielisz się, kiedy możesz 'Houston'.inList(['LA', 'New York', 'Houston']). Być może if (!String.prototype.inList) {String.prototype.inList = function(arr) {return arr.indexOf(this) >= 0};}lub za pomocą twojej whilemetody.
ErikE

0

Dziwi mnie, że nikt nie wspomniał o prostej funkcji, która pobiera ciąg znaków i listę.

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 zrobił dokładnie to, co sugerujesz , Sam, 27 sierpnia o godz. 1:29. W rzeczywistości moja wybrana odpowiedź jest prawie taka sama, po prostu dostarczając ciąg z tym, a nie parametrem.
ErikE

@ErikE Niestety, odpowiedź Jimsa wydawała mi się dziwna, jak powiedziałeś. Twoja zaakceptowana odpowiedź zwraca int, a ponieważ niektórzy ludzie mogą natknąć się na to pytanie w poszukiwaniu boolodpowiedzi. Pomyślałem, że może to pomóc kilku osobom.
Samuel Parkinson

0

Moje rozwiązanie daje następującą składnię:

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

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

I implementuję to poprzez rozszerzenie podstawowego prototypu Object, jak poniżej:

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

Alternatywnie możemy nazwać metodę isInArray (ale prawdopodobnie nie inArray) lub po prostu isIn.

Zalety: Prosty, prosty i samodokumentujący.


Może wystąpić problem z rozszerzeniem obiektu. bolinfest.com/javascript/inheritance.php w sekcji „Zespół Map Google nauczył się tego na własnej skórze ” i niezgodności z implementacją przeglądarki lub kodem innego użytkownika. Nadal uważam, że odpowiedź ErikE jest najlepsza, ponieważ iteracja po tablicy jest wolniejsza niż znalezienie klucza w haszapie po utworzeniu myValues[key];haszapy : gdzie myValues ​​jest obiektem, a klucz jest dowolnym ciągiem lub liczbą.
HMR,

-1

Uproszczona wersja odpowiedzi SLaks działa również:

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

.... ponieważ ciągi są rodzajem samych tablic. :)

W razie potrzeby zaimplementuj funkcję indexof dla Internet Explorera zgodnie z wcześniejszym opisem.


1
Będzie to działać tylko z łańcuchami jednoliterowymi, co NIE było zamierzone.
ErikE
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.