¿La forma más rápida de verificar que una cadena contenga otra subcadena en JavaScript?

163

Estoy trabajando con un problema de rendimiento en JavaScript. Entonces, solo quiero preguntar: ¿cuál es la forma más rápida de verificar si una cadena contiene otra subcadena (solo necesito el valor booleano)? ¿Podría sugerir su idea y un código de fragmento de muestra?

Đinh Hồng Châu
fuente
¿Está preguntando acerca de una subcadena fija o necesita una expresión regular (estoy un poco confundido por el uso de la regexetiqueta)?
Tim Pietzcker
1
Esta publicación sería útil ... stackoverflow.com/questions/1789945/javascript-string-contains
mtk
¿Qué tal dividir la cadena en una matriz alrededor del espacio en blanco y hacer una intersección de la matriz? stackoverflow.com/questions/1885557/…
giorgio79
jsben.ch/#/aWxtF
EscapeNetscape

Respuestas:

315

Tienes dos posibilidades:

  1. Expresión regular :

    (new RegExp('word')).test(str)
    // or
    /word/.test(str)
    
  2. indexOf:

    str.indexOf('word') !== -1

Las expresiones regulares parecen ser más rápidas (al menos en Chrome 10).

Prueba de rendimiento: pajar corto
Prueba de rendimiento: pajar largo


Actualización 2011:

No se puede decir con certeza qué método es más rápido. Las diferencias entre los navegadores son enormes. Mientras que en Chrome 10 indexOfparece ser más rápido, en Safari 5, indexOfes claramente más lento que cualquier otro método.

Tienes que ver y probar por ti mismo. Depende de tus necesidades. Por ejemplo, una búsqueda que no distingue entre mayúsculas y minúsculas es mucho más rápida con expresiones regulares.


Actualización 2018:

Solo para evitar que las personas ejecuten las pruebas ellos mismos, estos son los resultados actuales para los navegadores más comunes, los porcentajes indican un aumento en el rendimiento sobre el siguiente resultado más rápido (que varía entre navegadores):

Chrome: indexOf (~ 98% más rápido) <-- wow
Firefox: RegExp en caché (~ 18% más rápido)
IE11: RegExp en caché (~ 10% más rápido)
Edge: indexOf (~ 18% más rápido)
Safari: RegExp en caché (~ 0.4% más rápido)

Tenga en cuenta que RegExp en caché es: var r = new RegExp('simple'); var c = r.test(str);en lugar de:/simple/.test(str)

Felix Kling
fuente
3
Esto puede ser un poco más rápido solo si el texto a buscar se conoce de antemano (es decir, no se almacena en una variable) porque el motor JavaScript crea la expresión regular durante el tiempo de análisis. Si desea buscar una cadena contenida en una variable dentro de otra variable de cadena, indexOf es el más rápido porque necesitaría crear un objeto RegExp y procesar la cadena para escapar de caracteres especiales, etc.
Stephen Chung
por experiencia, indexOf puede ser más rápido para la búsqueda que no distingue entre mayúsculas y minúsculas si usa .toLowerCase en lo que sea que esté buscando primero
Hayk Saakian
Estoy escribiendo una aplicación de Office 2013, utilizando la API de JavaScript de Office de Microsoft, y el uso indexOfno funciona. No estoy seguro de por qué. Sin embargo, usar Regex sí. Este es un caso extremo, pero otros podrían encontrarse con el mismo problema.
Andy Mercer
¿Alguna razón substr () no es una de las posibles soluciones? Supongo que es mucho más rápido que la solución RegEx en muchas situaciones. Sin embargo, no sé cómo se compara con indexOf () (así que si lo omitió porque siempre funciona peor que indexOf (), entonces está bien, tal vez agregue una nota a ese efecto). EDITAR: este enlace JSperf muestra algunos interesantes resultados. Versión corta: indexOf () es el más rápido de todos los métodos, pero esto puede variar según la longitud de la cadena y cualquier patrón repetitivo.
Byson
1
@Bison: solo puedes usar substr si ya sabes dónde buscar. Me concentré solo en soluciones genéricas.
Felix Kling
17

¿Esto funciona para tí?

string1.indexOf(string2) >= 0

Editar: Esto puede no ser más rápido que un RegExp si la cadena2 contiene patrones repetidos. En algunos navegadores, indexOf puede ser mucho más lento que RegExp. Ver comentarios.

Edición 2: RegExp puede ser más rápido que indexOf cuando las cadenas son muy largas y / o contienen patrones repetidos. Ver comentarios y la respuesta de @ Felix.

Stephen Chung
fuente
pero, ¿cómo se compara esto con otros métodos? ¿Es este el más rápido, o es solo uno de los muchos métodos para hacerlo?
Chii
Esto debería ser rápido ya que está implementado por el propio JavaScript (es decir, ejecuta código nativo). Cualquier otro método basado en código JavaScript será más lento. Si conoce la cadena exacta, una expresión regular puede ser un poco más rápida (ya que el motor de JavaScript no tiene que recorrer la cadena de prototipos para encontrar .indexOf).
Stephen Chung
Si necesita una búsqueda que no distinga entre mayúsculas y minúsculas, entonces definitivamente necesitaría construir un objeto RegExp y llamar test.
Stephen Chung
3
Acabo de ejecutar una prueba en Safari. indexOfes una magnitud más lenta que cualquier otro método. Por lo tanto, en realidad no se puede decir qué método es más rápido. Varía de un navegador a otro.
Felix Kling
@Felix, esa es una buena observación (¡nunca confíes en nada hasta que realmente lo pruebes tú mismo!) Recuerdo algo que dice en cadenas con muchos patrones repetidos, las expresiones regulares deberían funcionar más rápido que una implementación simple de comparación de bucles porque las expresiones regulares se compilan en máquinas de estado y pueden retroceder mucho más rápido que los bucles simples, lo que siempre tiene que retroceder. seguimiento al siguiente personaje. ¡+1 por hacer el experimento y sacar esto a la luz!
Stephen Chung
17

El más rápido

  1. (ES6) incluye
    var cadena = "hola",
    substring = "lo";
    string.includes (subcadena);
  1. ES5 y el índice anterior
    var cadena = "hola",
    substring = "lo";
    string.indexOf (substring)! == -1;

http://jsben.ch/9cwLJ

ingrese la descripción de la imagen aquí

Tính Ngô Quang
fuente
8

En ES6, el includes()método se utiliza para determinar si una cadena se puede encontrar dentro de otra cadena, regresando trueo falsesegún corresponda.

var str = 'To be, or not to be, that is the question.';

console.log(str.includes('To be'));       // true
console.log(str.includes('question'));    // true
console.log(str.includes('nonexistent')); // false

Aquí está jsperf entre

var ret = str.includes('one');

Y

var ret = (str.indexOf('one') !== -1);

Como se muestra en jsperf, parece que ambos funcionan bien.

zangw
fuente
¿Puedo usar "regex" dentro, como incluye el argumento '? Al igual que: str.includes("x|y"); busque los literales "x" o "y" en la misma llamada.
ptkato
@Patrick, según el documento de inclusión, no puedes usarlo regex. Una str.includes("x") || str.includes('y')
solución
Como resultado de las mejoras de JavaScript de Chrome 59, indexOfes significativamente más rápido que includes(más de 1600% más rápido). No está claro cómo una diferencia de 44 millones de iteraciones / seg y 777+ millones de i / seg afecta el rendimiento del mundo real, sin embargo, es probable que los dispositivos móviles se beneficien lo suficiente como indexOfpara ser la opción ideal.
Chad Levy
7

Descubrí que usar un bucle for simple, iterar sobre todos los elementos de la cadena y comparar el uso se charAtrealiza más rápido que indexOfo Regex. El código y la prueba están disponibles en JSPerf .

ETA: indexOfy charAtambos funcionan de manera similar en Chrome Mobile según los datos del alcance del navegador que figuran en jsperf.com

wpg4665
fuente
Es extraño que una función hecha a mano sea mejor que una incorporada, pero supongo que esto se debe a que la aguja es solo un personaje. Aún así ...
Moss
Probado en Chrome Mobile 36.0.1985.57 en iPad de Apple (iOS 7.1.1). IndexOf es más rápido. Lo sentimos
rpax
@rpax CharAt sigue siendo significativamente más rápido en todas las plataformas (según el historial de jsperf), excepto en Chrome Mobile, donde tanto IndexOf como CharAt tienen un rendimiento muy pobre en comparación con el escritorio.
wpg4665
1
Me gustaría ver cómo funciona esto en NodeJS, y tampoco es un buen ejemplo porque solo estás buscando un personaje frente a una subcadena.
qodeninja
Esta no es una respuesta válida en absoluto. No está buscando una subcadena, solo la aparición de un solo carácter
Henrik Myntti,
3

Para encontrar una cadena simple, usar el método indexOf () y usar regex es más o menos lo mismo: http://jsperf.com/substring , así que elija el que parezca más fácil de escribir.

Chii
fuente
1

Es una manera fácil de usar el .match()método para encadenar.

var re = /(AND|OR|MAYBE)/;
var str = "IT'S MAYBE BETTER WAY TO USE .MATCH() METHOD TO STRING";
console.log('Do we found something?', Boolean(str.match(re)));

¡Le deseo un buen día, señor!

Anton Danilchenko
fuente
44
No hay razón para matchcuando hay un testmétodo ... Mira la respuesta principal.
Bergi