Визначте, чи рядок є у списку в JavaScript


262

У SQL ми можемо побачити, чи є рядок у такому списку:

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

Який хороший спосіб зробити це в JavaScript? Це так незграбно робити це:

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

І я не впевнений у ефективності чи чіткості цього:

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

Або можна скористатися функцією перемикання:

var str = 'a',
   flag = false;

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

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

Але це жахливий безлад. Будь-які ідеї?

У цьому випадку я повинен використовувати Internet Explorer 7, оскільки це стосується корпоративної сторінки інтрамережі. Так що ['a', 'b', 'c'].indexOf(str) !== -1не буде працювати на самому світі без синтаксичного цукру.


1
Чи можете ви пояснити, у чому саме різниця між "рядок є у списку" та "масив включає об'єкт"?
Michał Perłakowski

2
@Gothdo Тому, що список не завжди є масивом, а рядок не є об'єктом? Як це могло бути зрозуміліше?
ЕрікЕ

@ErikE , якщо це так , що ви згадали в NOTE , то це питання має бути закритим, не повинно бути далі Баунті / відповіді дозволені. Вже опублікованих відповідей достатньо, щоб кожен міг отримати допомогу.
Vikasdeep Singh

@VicJordan Можливо, ви хочете видалити свій коментар, оскільки він більше не застосовується.
ЕрікЕ

Відповіді:


279

Ви можете зателефонувати indexOf:

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

15
Єдина проблема в Array.prototype.indexOfтому, що він не працюватиме на IE, на жаль, навіть IE8 не вистачає цього методу.
CMS

2
Але ви можете визначити це, якщо хочете. Дивіться soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer
Джейсон Холл

Ось "справжні" товари: developer.mozilla.org/uk/Core_JavaScript_1.5_Reference/…
ErikE

8
@ImJasonH: Код на цій сторінці дійсно поганий IMHO, наприклад, перевірка, чи Array.indexOfіснує, перш ніж переосмислити, Array.prototype.indexOfякі не є тим самим. Я б рекомендував реалізацію Mozilla, доступну тут: developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/…
CMS

1
Слухайте @CMS, @Emtucifor, реалізація Mozilla набагато краща.
Зал Джейсона

230

EcmaScript 6

Якщо ви використовуєте ES6, ви можете сконструювати масив елементів та використовувати includes:

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

Це має деякі переваги по порівнянні з властивими , indexOfоскільки він може належним чином перевірити на наявність NaNв списку, і може відповідати відсутньою елементів масиву , таким як середні один в [1, , 2]к undefined. includesтакож працює на набраних JavaScript масивах, таких як Uint8Array.

Якщо ви стурбовані підтримкою веб-переглядача (наприклад, для IE або Edge), ви можете перевірити Array.includesв CanIUse.Com , і якщо ви хочете націлити на браузер або версію браузера, яка відсутня includes, я рекомендую polyfill.io для поліфілінгу.

Без масиву

Ви можете додати нове isInListвластивість до рядків таким чином:

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

Потім використовуйте його так:

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

Можна зробити те ж саме і для Number.prototype.

Array.indexOf

Якщо ви використовуєте сучасний браузер, indexOfзавжди працює. Однак для IE8 і більш ранніх вам знадобиться поліфіл.

Якщо indexOfповертається -1, елемент не знаходиться в списку. Однак майте на увазі, що цей метод не буде належним чином перевіряти NaN, і хоча він може відповідати явним undefined, він не може відповідати відсутньому елементу, undefinedяк у масиві [1, , 2].

Polyfill для indexOfабо includesв IE або будь-який інший браузер / версія відсутня підтримка

Якщо ви не хочете користуватися такою послугою, як polyfill.io, як згадувалося вище, ви завжди можете включити у свій власний вихідний код спеціальні спеціальні поліфіли. Наприклад, Мережа розробників Mozilla має для indexOf.

У цій ситуації, коли мені довелося зробити рішення для Internet Explorer 7, я "прокрутив власну" більш просту версію indexOf()функції, яка не відповідає стандартам:

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

Однак я не думаю, що модифікація Array.prototypeє найкращою відповіддю в довгостроковій перспективі. Модифікація Objectта Arrayпрототипи в JavaScript може призвести до серйозних помилок. Вам потрібно вирішити, чи безпечно це робити у власному оточенні. Основне зауваження полягає в тому, що ітерація масиву (коли Array.prototype додає властивості) з for ... inповерне нове ім'я функції в якості однієї з клавіш:

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!

Ваш код може працювати зараз, але коли хтось у майбутньому додасть сторонній бібліотеку JavaScript або плагін, який ревно не захищає від успадкованих ключів, все може зламатися.

Старий спосіб уникнути поломки - це під час перерахування перевіряти кожне значення, щоб побачити, чи об'єкт насправді має його як не успадковане властивість if (arr.hasOwnProperty(x))і лише потім працювати з цим x.

Новий ES6 спосіб уникнути цього додаткового ключа проблеми полягає у використанні ofзамість in, for (let x of arr). Однак, якщо ви не зможете гарантувати, що всі ваші кодові та сторонні бібліотеки суворо дотримуються цього методу, то для цілей цього питання ви, ймовірно, просто захочете використовувати, includesяк зазначено вище.


2
ось ще один хороший PolyFill для indexOfкомпанії MDN. Це в основному те саме, але з парою коротких замикань для легкої оцінки.
KyleMit

1
Downvoter: будь ласка, прокоментуйте. У чому проблема з цим? Ця функція НЕ відповідає стандартам і не намагається бути такою.
ЕрікЕ

Вам слід скористатися чимось уже перевіреним, як поліфіл, відзначений посиланням @KyleMit: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
lmiguelmh

@lmiguelmh Як щодо "polyfill", на який я сам розмістив посилання, від самих документів Mozilla? У будь-якому випадку ця функція настільки мертва, що мене дуже не турбує тестування. І кожен, хто є, не повинен виконувати «вже перевірену» функцію, але сам повинен перевіряти те, що вони використовують. Тож ваш коментар трохи недоречний. Ви визначили конкретний дефект з моєю функцією?
ЕрікЕ

@ErikE Ви прав, ця функція мертва, і кожен, хто використовує вашу відповідь, повинен це знати. В першу чергу я не бачив вашого посилання, тому що шукав "відповіді"
lmiguelmh

53

Більшість відповідей передбачає Array.prototype.indexOfметод, єдина проблема полягає в тому, що він не працюватиме в будь-якій версії IE до IE9.

В якості альтернативи я залишаю вам ще два варіанти, які працюватимуть у всіх браузерах:

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


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

Хм, дякую, що згадуєте про це, CMS. Так буває, що це для корпоративного інтрамережі, і вони використовують ... здогадайтесь, що ... IE. Оскільки метод регулярного вираження надає мені воллі, мені доведеться або зробити функцію, що циклічно, або використовувати метод об'єкта, який я запропонував у своєму пості (третій блок коду).
ЕрікЕ

13
Це буде відповідати "HiFooThere"- я б /^(?:Foo|Bar|Baz)$/замість цього перейшов (початок рядка, група, яка не захоплює, кінець рядка).
TrueWill

indexOfтепер підтримується в IE 9 і вище відповідно до MDN .
крок

28

Масиви мають indexOfметод, за допомогою якого можна шукати рядки:

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

3
У старих браузерах це не вдасться.
epascarello

На жаль, згадка про те, що це не працює в IE, було б добре. :)
ErikE

2
@epascarello: Не тільки в старих браузерах він не працює в будь-якому IE, навіть в IE8 :(
CMS

12

Я використовував трюк

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

Ви могли зробити щось подібне

>>> 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, використовує "в" будь-який швидший / повільніше / краще / гірше, ніж просто намагатися знеструмити елемент, як показав мій третій блок коду в моєму запитанні? Крім того, якщо ви збираєтеся переглядати річ, я думаю, ви можете також перевірити, чи є елемент у масиві в той час ... просто переведіть цикл у функцію. А для чого варто var i=a.length;while (i--) {/*use a[i]*/}- це найшвидший цикл (якщо прийнятний зворотний порядок).
ЕрікЕ

@Emtucifor: це дійсно залежить від того, що ти робиш, і я думаю, що це може працювати по-різному в різних двигунах javascript. Якщо ваші дані потребують в будь-який момент використання словника, то краще створити його таким чином. Я думаю, що це буде швидше через деталі впровадження на двигунах (використання хеш-таблиць для об'єкта), коли буде створений об'єкт dict .
Естебан Кюбер

У JavaScript це не називається диктатом, воно називається об'єктом .
Соломон Учко

8

Ось моя:

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

Це швидше, якщо ви добре з передачею масиву:

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

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

Ось jsperf: http://jsperf.com/bmcgin-inlsit


Чесно кажучи, той, де ви передаєте масив, мабуть, буде більш корисним, і він, мабуть, повинен бути на Arrayросійському прототипі: можливо, щось подібне Array.prototype.contains.
Соломон Учко

6

RegExpє універсальним, але я розумію, що ви працюєте з масивами. Отже, перевірте такий підхід. Я використовую, щоб використовувати його, і це дуже ефективно і швидко палає!

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

rx.test(str);

Ви також можете застосувати деякі модифікації, тобто:

Однолінійний

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

Справа нечутлива

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


І багато інших!


Використання регулярного вираження вимагає уникати купки спеціальних символів. Це також менш зрозуміло. Я не думаю, що це вдале рішення.
ЕрікЕ

@ErikE Я розумію ваші застереження, тому я оновив свою відповідь, додавши ще один код, який не використовує RegExp, він ретросумісний з IE, дуже ідіоматичний і швидкий.
sospedra

Ваш додатковий код є майже копією моєї відповіді, яку я надав за 5 років до того, як ви вирішили опублікувати рішення Regex. Дякую за участь, і в той же час я вважаю, що найкраще повернутись до вашої попередньої відповіді.
ЕрікЕ

@ErikE так, ти маєш рацію, я не використовую для перевірки відповідей на запитання. І я згоден, це дійсно схоже.
sospedra

6

Використання indexOf (не працює з IE8).

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

Для підтримки IE8 ви можете реалізувати Mozilla indexOf.

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

Регулярні вирази через String.prototype.match (документи).

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

}

1
Чи помічаєте ви, що ви точно копіюєте інші відповіді на сторінці? Це не додає значення.
ЕрікЕ

5

Схоже, вам потрібно використовувати функцію in_array.

jQuery -> inArray

Прототип -> Array.indexOf

Або подивіться ці приклади, якщо ви не використовуєте jQuery або прототип:

Стилістична примітка: змінні, названі thisthing thatthing, повинні бути названі, щоб сказати вам щось про те, що вони містять (іменник).


О, вони не були змінними, але мали на увазі випадкові заповнювачі для виразів ... лише приклад того, як я планував використовувати сценарій.
ЕрікЕ

5

На додаток до indexOf(що запропонували інші плакати), використання прототипу Enumerable.include () може зробити це більш охайним і стислим:

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

5
Enumerable.include () був застарілим, але Array.prototype.includes () надходить і вже працює в більшості браузерів, крім IE (звичайно) та Edge (звичайно).
біла борода

2

Дякуємо за запитання та рішення за допомогою методу Array.indexOf.

Я використовував код з цього рішення, щоб створити функцію inList (), яка б, IMO, спростила написання і читання було зрозумілішим:

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

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

ВИКОРИСТАННЯ:

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

1
Літеральні масиви Javascript - це так просто, я не бачу, чому ви поділилися б, коли могли 'Houston'.inList(['LA', 'New York', 'Houston']). Можливо, if (!String.prototype.inList) {String.prototype.inList = function(arr) {return arr.indexOf(this) >= 0};}або використовуючи свій whileметод.
ЕрікЕ

0

Я здивований, що ніхто не згадав просту функцію, яка займає рядок і список.

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

Джим зробив саме те, що ви запропонували , Сем, 27 серпня 1111 о 1:29. Насправді, моя вибрана відповідь - це майже те саме, що просто постачає рядок цим параметром, а не параметром.
ErikE

@ErikE Вибачте, відповідь Джимса здавалася мені дивною, як ви сказали. І ваша прийнята відповідь повертає відповідь int, де, як деякі люди можуть зіткнутися з цим питанням, шукаючи boolповернення. Думав, що це може допомогти декільком людям.
Самуель Паркінсон

0

Моє рішення призводить до такого синтаксису:

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

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

І я реалізую це шляхом розширення базового прототипу Object, наприклад, такого:

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

Ми можемо також назвати метод isInArray (але, мабуть, не inArray) або просто isIn.

Переваги: ​​просте, зрозуміле та самодокументування.


Можуть виникнути проблеми із розширенням об’єкта. bolinfest.com/javascript/inheritance.php у розділі "Команда Карт Google навчився цьому важким шляхом" та несумісність із реалізацією браузера чи іншим кодом користувача. Я все ще думаю, що відповідь ErikE є найкращою, оскільки ітерація над масивом відбувається повільніше, ніж пошук ключа в хешмапі після створення хешмапу: myValues[key];де myValues ​​- це об'єкт, а ключ - будь-який рядок або число.
HMR

-1

Спрощена версія відповіді SLaks також працює:

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

.... оскільки рядки - це свого роду масиви. :)

Якщо потрібно, реалізуйте функцію indexof для Internet Explorer, як описано раніше.


1
Це буде працювати лише з однобуквенними рядками, які НЕ були призначені.
ErikE
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.