Determinar si la cadena está en la lista en JavaScript


263

En SQL podemos ver si una cadena está en una lista así:

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

¿Cuál es una buena manera de hacer esto en JavaScript? Es tan torpe hacer esto:

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

Y no estoy seguro sobre el rendimiento o la claridad de esto:

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

O uno podría usar la función de cambio:

var str = 'a',
   flag = false;

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

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

Pero eso es un desastre horrible. ¿Algunas ideas?

En este caso, tengo que usar Internet Explorer 7 ya que es para una página de intranet corporativa. Por ['a', 'b', 'c'].indexOf(str) !== -1lo tanto , no funcionará de forma nativa sin un poco de azúcar de sintaxis.


1
¿Podría explicar cuál es exactamente la diferencia entre "la cadena está en la lista" y "la matriz incluye un objeto"?
Michał Perłakowski

2
@Gothdo ¿Porque una lista no siempre es una matriz y una cadena no es un objeto? ¿Cómo podría ser más claro?
ErikE

@ErikE si este es el caso que mencionó en la NOTA, entonces esta pregunta debería cerrarse, no debería haber más recompensas / respuestas permitidas. Las respuestas ya publicadas son suficientes para que cualquiera pueda obtener ayuda.
Vikasdeep Singh

@VicJordan Quizás le gustaría eliminar su comentario ya que ya no se aplica.
ErikE

Respuestas:


280

Puedes llamar a indexOf:

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

15
El único problema Array.prototype.indexOfes que no funcionará en IE, lamentablemente incluso IE8 carece de este método.
CMS



8
@ImJasonH: El código en esa página es realmente muy malo en mi humilde opinión, por ejemplo, la verificación si Array.indexOfexiste antes de anular, Array.prototype.indexOfque no son lo mismo. Recomendaría la implementación realizada por Mozilla disponible aquí: developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/…
CMS

1
Escuche @CMS, @Emtucifor, la implementación de Mozilla es mucho mejor.
Jason Hall

231

EcmaScript 6

Si está utilizando ES6, puede construir una matriz de los elementos y usar includes:

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

Esto tiene algunas ventajas inherentes sobre indexOfporque puede probar correctamente la presencia de NaNla lista, y puede igualar faltan elementos de la matriz como la del medio en [1, , 2]que undefined. includestambién funciona en matrices escritas en JavaScript como Uint8Array.

Si le preocupa la compatibilidad con el navegador (como IE o Edge), puede consultar Array.includesen CanIUse.Com , y si desea apuntar a un navegador o una versión de navegador que falta includes, recomiendo polyfill.io para polyfilling.

Sin una matriz

Puede agregar una nueva isInListpropiedad a las cadenas de la siguiente manera:

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

Luego úsalo así:

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

Puedes hacer lo mismo por Number.prototype.

Array.indexOf

Si está utilizando un navegador moderno, indexOfsiempre funciona. Sin embargo, para IE8 y versiones anteriores, necesitará un polyfill.

Si indexOfdevuelve -1, el elemento no está en la lista. Sin embargo, tenga en cuenta que este método no verificará correctamente NaNy, aunque puede coincidir con un explícito undefined, no puede coincidir con un elemento que falta undefinedcomo en la matriz [1, , 2].

Polyfill para indexOfo includesen IE, o cualquier otro navegador / versión que carece de soporte

Si no desea utilizar un servicio como polyfill.io como se mencionó anteriormente, siempre puede incluir en su propio código fuente polyfills personalizados que cumplan con los estándares. Por ejemplo, Mozilla Developer Network tiene uno para indexOf.

En esta situación en la que tuve que hacer una solución para Internet Explorer 7, "desarrollé mi propia versión" más simple de la indexOf()función que no cumple con los estándares:

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

Sin embargo, no creo que modificar Array.prototypesea ​​la mejor respuesta a largo plazo. La modificación Objecty los Arrayprototipos en JavaScript pueden provocar errores graves. Debe decidir si hacerlo es seguro en su propio entorno. La nota principal es que iterar una matriz (cuando Array.prototype ha agregado propiedades) for ... indevolverá el nuevo nombre de la función como una de las teclas:

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!

Su código puede funcionar ahora, pero en el momento en que alguien en el futuro agregue una biblioteca de JavaScript de terceros o un complemento que no esté protegiendo celosamente contra las claves heredadas, todo puede romperse.

La forma antigua de evitar esa ruptura es, durante la enumeración, verificar cada valor para ver si el objeto realmente lo tiene como una propiedad no heredada con if (arr.hasOwnProperty(x))y solo luego trabajar con eso x.

La nueva forma ES6 para evitar este problema llave extra-es utilizar ofen lugar de in, for (let x of arr). Sin embargo, a menos que pueda garantizar que todo su código y las bibliotecas de terceros se adhieran estrictamente a este método, a los fines de esta pregunta, es probable que solo desee usar includescomo se indicó anteriormente.


2
aquí hay otra buena polyfill para indexOfproporcionada por MDN. Básicamente hace lo mismo pero con un par de cortocircuitos para una evaluación fácil.
KyleMit

1
Votante: comente por favor. ¿Cuál es el problema con esto? Esta función NO cumple con los estándares y no intenta serlo.
ErikE

Debería usar algo ya probado como el polyfill señalado por el enlace @KyleMit: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
lmiguelmh

@lmiguelmh ¿Qué tal el "polyfill" al que yo mismo publiqué un enlace, desde los propios documentos de Mozilla? En cualquier caso, esta función es tan simple que realmente no estoy demasiado preocupado por las pruebas. Y cualquiera que lo sea, no debe tomar una función "ya probada", sino que debe probar lo que sea que use. Entonces tu comentario está un poco fuera de lugar. ¿Has identificado un defecto específico con mi función?
ErikE

@ErikE tienes razón, esta función está muerta y cualquiera que use tu respuesta debería saberlo. No vi su enlace en primer lugar porque estaba buscando "respuestas"
lmiguelmh

53

La mayoría de las respuestas sugieren el Array.prototype.indexOfmétodo, el único problema es que no funcionará en ninguna versión de IE antes de IE9.

Como alternativa, te dejo dos opciones más que funcionarán en todos los navegadores:

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


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

Hmmm, gracias por mencionar eso, CMS. Sucede que esto es para una intranet corporativa y usan ... adivina qué ... IE. Dado que el método de expresión regular me da las intenciones, tendré que hacer una función que se repita o usar el método de objeto que sugerí en mi publicación (tercer bloque de código).
ErikE

13
Esto coincidirá "HiFooThere": en su lugar, iría con /^(?:Foo|Bar|Baz)$/(inicio de cadena, grupo sin captura, final de cadena).
TrueWill

indexOfahora es compatible con IE 9 y superior de acuerdo con MDN .
krock

28

Las matrices tienen un indexOfmétodo que puede usarse para buscar cadenas:

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

3
Esto fallará en los navegadores más antiguos.
epascarello

Vaya ... mencionar que esto no funciona en IE hubiera sido bueno. :)
ErikE

2
@epascarello: No solo en navegadores antiguos, fallará en cualquier IE, incluso en IE8 :(
CMS

12

Un truco que he usado es

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

Podrías hacer algo como

>>> 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, ¿está usando "in" más rápido / más lento / mejor / peor que simplemente intentar desreferenciar el elemento como mostró mi tercer bloque de código en mi pregunta? Además, si vas a recorrer la cosa, creo que también puedes verificar si el elemento está en la matriz en ese momento ... simplemente ajusta el ciclo en una función. Y para lo que vale la pena var i=a.length;while (i--) {/*use a[i]*/}es el método de bucle más rápido (si el orden inverso es aceptable).
ErikE

@Emtucifor: realmente depende de lo que esté haciendo, y supongo que podría funcionar de manera diferente en diferentes motores de JavaScript. Si sus datos necesitarían en algún momento el uso de un diccionario, entonces es mejor crearlos de esta manera. Creo que esto será más rápido debido a los detalles de implementación en los motores (el uso de tablas hash para el objeto) una vez que se crea el objeto dict .
Esteban Küber

En JavaScript, no se llama un dict, se llama un objeto .
Solomon Ucko

8

Aquí está el mío:

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

Este es más rápido si está de acuerdo con pasar una matriz:

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

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

Aquí está el jsperf: http://jsperf.com/bmcgin-inlsit


Francamente, el que pasa una matriz probablemente sería más útil, y probablemente debería estar en Arrayel prototipo: tal vez algo así Array.prototype.contains.
Solomon Ucko

6

RegExpes universal, pero entiendo que estás trabajando con matrices. Entonces, mira este enfoque. Solía ​​usarlo, ¡y es muy efectivo y extremadamente rápido!

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

rx.test(str);

También puede aplicar algunas modificaciones, es decir:

Un trazador de líneas

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

Insensible a mayúsculas y minúsculas

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


¡Y muchos otros!


El uso de expresiones regulares requiere evitar un montón de caracteres especiales. También es menos claro. No creo que sea una buena solución.
ErikE

@ErikE Entiendo sus reservas, así que actualicé mi respuesta agregando otro código que no usa RegExp, es retrocompatible con IE, muy idiomático y rápido.
sospedra

Su código adicional es casi un duplicado de mi respuesta que le proporcioné 5 años antes de que decidiera publicar una solución Regex. Gracias por participar y, al mismo tiempo, creo que es mejor volver a su respuesta anterior.
ErikE

@ErikE sí, tienes razón, no suelo buscar respuestas en la pregunta. Y estoy de acuerdo en que es muy similar.
sospedra

6

Usando indexOf (no funciona con IE8).

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

Para admitir IE8, puede implementar el indexOf de Mozilla.

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

Expresiones regulares a través de String.prototype.match (docs).

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

}

1
¿Te das cuenta de que estás duplicando exactamente otras respuestas en la página? Esto no agrega ningún valor.
ErikE

5

Parece que necesita usar la función in_array.

jQuery -> inArray

Prototipo -> Array.indexOf

O vea estos ejemplos si no está utilizando jQuery o Prototype:

Nota estilística: las variables llamadas thisthing thatthing, deben nombrarse para decirle algo sobre lo que contienen (sustantivo).


Oh, no eran variables, sino que se pensaban como marcadores de posición aleatorios para expresiones ... solo un ejemplo de cómo planeaba usar el script.
ErikE

5

Además de indexOf(que otros carteles han sugerido), el uso de Enumerable.include () del prototipo puede hacer que esto sea más claro y conciso:

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

55
Enumerable.include () ha quedado en desuso, pero Array.prototype.includes () está llegando y ya está funcionando en la mayoría de los navegadores, excepto IE (por supuesto) y Edge (por supuesto).
barba blanca

2

Gracias por la pregunta y la solución usando el método Array.indexOf.

Usé el código de esta solución para crear una función inList () que, en mi opinión, simplificaría la escritura y la lectura sería más clara:

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
Los literales de matriz de Javascript son tan fáciles que no veo por qué se dividiría cuando podría hacerlo 'Houston'.inList(['LA', 'New York', 'Houston']). Quizás if (!String.prototype.inList) {String.prototype.inList = function(arr) {return arr.indexOf(this) >= 0};}o usando tu whilemétodo.
ErikE

0

Me sorprende que nadie haya mencionado una función simple que tome una cadena y una lista.

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 hizo exactamente lo que sugieres , Sam, el 27 de agosto del 11 a las 1:29. De hecho, mi respuesta seleccionada es más o menos lo mismo, solo suministrando la cadena con esto en lugar de un parámetro.
ErikE

@ErikE Lo siento, la respuesta de Jims me pareció extraña como dijiste. Y su respuesta aceptada devuelve un int, donde algunas personas podrían encontrar esta pregunta en busca de una respuesta bool. Pensé que podría ayudar a algunas personas.
Samuel Parkinson

0

Mi solución da como resultado una sintaxis como esta:

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

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

Y lo implemento extendiendo el prototipo básico de Objeto, así:

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

Alternativamente, podríamos nombrar el método isInArray (pero probablemente no en Array) o simplemente isIn.

Ventajas: simple, directo y autodocumentado.


Podría haber problemas para extender Object. bolinfest.com/javascript/inheritance.php bajo "El equipo de Google Maps aprendió esto de la manera difícil" e incompatibilidad con la implementación del navegador u otro código de usuario. Todavía creo que la respuesta de ErikE es la mejor, ya que iterar sobre una matriz es más lento que encontrar una clave en un hashmap una vez que se crea el hashmap: myValues[key];donde myValues ​​es un objeto y la clave es cualquier cadena o número.
HMR

-1

Una versión simplificada de la respuesta de SLaks también funciona:

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

.... ya que las cadenas son una especie de matrices en sí mismas. :)

Si es necesario, implemente la función indexof para Internet Explorer como se describió antes que yo.


1
Esto solo funcionará con cadenas de una letra, lo cual NO fue diseñado.
ErikE
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.