Determinar si la cadena está en la lista en JavaScript
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 muy complicado hacer esto:
if (expression1 || expression2 || str === 'a' || str === 'b' || str === 'c') {
// do something
}
Y no estoy seguro del rendimiento o la claridad de esto:
if (expression1 || expression2 || {a:1, b:1, c:1}[str]) {
// do something
}
O se 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 lo tanto, ['a', 'b', 'c'].indexOf(str) !== -1
no funcionará de forma nativa sin un poco de azúcar en la sintaxis.
ES6 (ES2015) y superiores
Si está utilizando ECMAScript 6 (también conocido como ES2015) o superior, la forma más limpia es construir una matriz de elementos y usar Array.includes
:
['a', 'b', 'c'].includes('b')
Esto tiene algunos beneficios inherentes indexOf
porque puede probar adecuadamente la presencia de NaN
en la lista y puede hacer coincidir los elementos faltantes de la matriz, como el del medio, [1, , 2]
en undefined
. También trata a +0
y -0
como iguales. includes
También funciona en matrices escritas en JavaScript como Uint8Array
.
Si le preocupa la compatibilidad del navegador (como IE o Edge), puede consultar Array.includes
en CanIUse.Com , y si desea apuntar a un navegador o a una versión del navegador que falta includes
, deberá realizar la transpilación a una versión inferior de ECMAScript. utilizando una herramienta como Babel, o incluya un script de polyfill en el navegador, como los disponibles en polyfill.io .
Mayor rendimiento
Tenga en cuenta que no hay garantía de que Array.includes()
el tiempo de ejecución no aumente con la cantidad de elementos de la matriz: puede tener un rendimiento O(n). Si necesita un mayor rendimiento y no construirá el conjunto de elementos repetidamente (pero verificará repetidamente si los elementos contienen algún elemento), debe usar a Set
porque la especificación ES requiere que las implementaciones de Set
(y Map
también) sean sub -lineal para lecturas:
La especificación requiere que se implementen conjuntos "que, en promedio, proporcionen tiempos de acceso sublineales en cuanto al número de elementos de la colección". Por lo tanto, podría representarse internamente como una tabla hash (con búsqueda O(1)), un árbol de búsqueda (con búsqueda O(log(N)), o cualquier otra estructura de datos, siempre que la complejidad sea mejor que O (NORTE).
const interestingItems = new Set(['a', 'b', 'c'])
const isItemInSet = interestingItems.has('b')
Tenga en cuenta que puede pasar cualquier elemento iterable al Set
constructor (cualquier cosa que admita for...of ). También puedes convertir a Set
en una matriz usándolo Array.from(set)
o difundiéndolo [...set]
.
Sin una matriz
Realmente no se recomienda esto, pero puede agregar una nueva isInList
propiedad a las cadenas de la siguiente manera:
if (!String.prototype.isInList) {
Object.defineProperty(String.prototype, 'isInList', {
get: () => function(...args) {
let value = this.valueOf();
for (let i = 0, l = args.length; i < l; i += 1) {
if (arguments[i] === value) return true;
}
return false;
}
});
}
Entonces úsalo así:
'fox'.isInList('weasel', 'fox', 'stoat') // true
'fox'.isInList('weasel', 'stoat') // false
Puedes hacer lo mismo para Number.prototype
.
Tenga en cuenta que Object.defineProperty
no se puede utilizar en IE8 y versiones anteriores, ni en versiones muy antiguas de otros navegadores. Sin embargo, es una solución muy superior porque String.prototype.isInList = function() { ... }
el uso de una asignación simple como esa creará una propiedad enumerable en String.prototype
, que es más probable que rompa el código.
matriz.indexOf
Si estás utilizando un navegador moderno, indexOf
siempre funciona. Sin embargo, para IE8 y versiones anteriores necesitarás un polyfill.
Si indexOf
devuelve -1, el elemento no está en la lista. Sin embargo, tenga en cuenta que este método no comprobará correctamente NaN
y, si bien puede coincidir con un elemento explícito undefined
, no puede hacer coincidir un elemento faltante con un elemento undefined
como en la matriz [1, , 2]
.
Polyfill para indexOf
o includes
en IE, o cualquier otro navegador/versión que no sea compatible
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, la biblioteca CoreJs tiene una implementación de indexOf
.
En esta situación en la que tuve que crear 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;
}
}
Notas sobre la modificación de prototipos de objetos
Sin embargo, no creo que modificarlo String.prototype
sea Array.prototype
una buena estrategia a largo plazo. La modificación de prototipos de objetos en JavaScript puede provocar errores graves. Debe decidir si hacerlo es seguro en su propio entorno. Lo más importante es que al iterar una matriz (cuando Array.prototype tiene propiedades agregadas) se for ... in
devolverá el nuevo nombre de la función como una de las claves:
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!
Es posible que su código funcione ahora, pero en el momento en que alguien en el futuro agregue una biblioteca o complemento de JavaScript de terceros que no proteja celosamente contra las claves heredadas, todo puede fallar.
La antigua forma de evitar esa rotura es, durante la enumeración, verificar cada valor para ver si el objeto realmente lo tiene como una propiedad no heredada if (arr.hasOwnProperty(x))
y solo entonces trabajar con eso x
.
Las nuevas formas de ES6 para evitar este problema de clave adicional son:
Úselo
of
en lugar dein
,for (let x of arr)
. Sin embargo, dependiendo del objetivo de salida y las configuraciones/capacidades exactas de su transpilador de nivel inferior, esto puede no ser confiable. Además, a menos que pueda garantizar que todo su código y bibliotecas de terceros se ajusten estrictamente a este método, para los fines de esta pregunta probablemente solo desee utilizarloincludes
como se indicó anteriormente.Defina sus nuevas propiedades en el prototipo usando
Object.defineProperty()
, ya que esto hará que la propiedad (por defecto) no sea enumerable. Esto sólo resuelve realmente el problema si todas las bibliotecas o módulos de JavaScript que utiliza también lo hacen.
Tenga en cuenta un último problema
Por último, tenga en cuenta que, si bien los polyfills tienen sentido y modificar los prototipos de objetos es una estrategia útil, en ocasiones todavía puede haber problemas de alcance con ese enfoque.
En un navegador, cada document
objeto distinto tiene su propio alcance global, y en el navegador JS es posible crear nuevos documentos (como los que se usan para renderizar fuera de la pantalla o para crear fragmentos de documentos) u obtener una referencia al objeto de otra página document
. (como a través de comunicación entre páginas utilizando un enlace de destino con nombre), por lo que es posible en ciertas (¿raras?) circunstancias que los prototipos de objetos no tengan los métodos que usted espera que tengan, aunque siempre puede ejecutar sus polyfills nuevamente contra el nuevos objetos globales...
En Node.js, modificar prototipos de global
objetos puede ser seguro, pero modificar los prototipos de objetos importados no globales podría provocar roturas si alguna vez se requieren/importan dos versiones del mismo paquete, porque las importaciones de los dos Las versiones no expondrán los mismos objetos, por lo que no tendrán los mismos prototipos de objetos. Es decir, su código podría funcionar bien hasta que una dependencia o subdependencia use una versión diferente a la que espera, y sin que su propio código cambie, un simple npm install
o yarn install
podría desencadenar este problema. (Hay opciones para solucionar esto, como resolutions
la propiedad de hilo en package.json
, pero no es bueno confiar en eso si tiene otras opciones).
Puedes llamar indexOf
:
if (['a', 'b', 'c'].indexOf(str) >= 0) {
//do something
}
La mayoría de las respuestas sugieren el Array.prototype.indexOf
método, el único problema es que no funcionará en ninguna versión de IE anterior a IE9.
Como alternativa os dejo dos opciones más que funcionarán en todos los navegadores:
if (/Foo|Bar|Baz/.test(str)) {
// ...
}
if (str.match("Foo|Bar|Baz")) {
// ...
}