¿Cómo contar la ocurrencia de cadena en cadena?

609

¿Cómo puedo contar la cantidad de veces que una cadena en particular ocurre en otra cadena? Por ejemplo, esto es lo que estoy tratando de hacer en Javascript:

var temp = "This is a string.";
alert(temp.count("is")); //should output '2'
TruMan1
fuente
19
Depende de si acepta instancias superpuestas , por ejemplo, var t = "sss"; ¿Cuántas instancias de la subcadena "ss" hay en la cadena de arriba? ¿1 o 2? ¿Saltas sobre cada instancia o mueves el puntero carácter por carácter, buscando la subcadena?
Tim
44
Un punto de referencia mejorado para las respuestas de esta pregunta: jsperf.com/string-ocurrence-split-vs-match/2 (basado en el punto de referencia de Kazzkiq).
idmean

Respuestas:

1030

La gexpresión regular (abreviatura de global ) dice buscar en toda la cadena en lugar de solo encontrar la primera aparición. Esto coincide isdos veces:

var temp = "This is a string.";
var count = (temp.match(/is/g) || []).length;
console.log(count);

Y, si no hay coincidencias, devuelve 0:

var temp = "Hello World!";
var count = (temp.match(/is/g) || []).length;
console.log(count);

Rebecca Chernoff
fuente
3
moderno y elegante, pero la solución de Vitimtk es mucho más eficiente. ¿Qué piensan todos de su código?
TruMan1
55
Esto responde mejor a la pregunta. Si alguien pregunta "¿Cómo puedo hacer esto 10 veces más rápido en un caso especial (sin expresiones regulares)" Vitimtk ganaría esa pregunta.
Dzhaughn
121
Gracias por esto ... Fui count = (str.match(/is/g) || []).lengtha manejar si no tienes una coincidencia.
Matt
66
No creo que esta respuesta coincida correctamente con la pregunta, porque no toma una cadena como argumento para coincidir, como describe el caso de uso. Claro, puede crear dinámicamente la expresión regular utilizando el RegExpconstructor y pasando la cadena que está buscando, pero en ese caso debe escapar de todos los metacaracteres. En ese escenario, es preferible un enfoque de cadena pura.
ZER0
3
¡La respuesta de Matt debería estar en la respuesta!
Senči
240
/** Function that count occurrences of a substring in a string;
 * @param {String} string               The string
 * @param {String} subString            The sub string to search for
 * @param {Boolean} [allowOverlapping]  Optional. (Default:false)
 *
 * @author Vitim.us https://gist.github.com/victornpb/7736865
 * @see Unit Test https://jsfiddle.net/Victornpb/5axuh96u/
 * @see http://stackoverflow.com/questions/4009756/how-to-count-string-occurrence-in-string/7924240#7924240
 */
function occurrences(string, subString, allowOverlapping) {

    string += "";
    subString += "";
    if (subString.length <= 0) return (string.length + 1);

    var n = 0,
        pos = 0,
        step = allowOverlapping ? 1 : subString.length;

    while (true) {
        pos = string.indexOf(subString, pos);
        if (pos >= 0) {
            ++n;
            pos += step;
        } else break;
    }
    return n;
}

Uso

occurrences("foofoofoo", "bar"); //0

occurrences("foofoofoo", "foo"); //3

occurrences("foofoofoo", "foofoo"); //1

allowOverlapping

occurrences("foofoofoo", "foofoo", true); //2

Partidos:

  foofoofoo
1 `----´
2    `----´

Prueba de unidad

Punto de referencia

Hice una prueba de referencia y mi función es más de 10 veces más rápida que la función de coincidencia regexp publicada por gumbo. En mi cadena de prueba es de 25 caracteres de longitud. con 2 ocurrencias del caracter 'o'. Ejecuté 1 000 000 de veces en Safari.

Safari 5.1

Benchmark> Tiempo total de ejecución: 5617 ms (regexp)

Benchmark> Ejecución de tiempo total: 881 ms (mi función 6.4x más rápido)

Firefox 4

Benchmark> Tiempo total de ejecución: 8547 ms (Rexexp)

Benchmark> Ejecución de tiempo total: 634 ms (mi función 13.5x más rápido)


Editar: cambios que he realizado

  • longitud de la subcadena en caché

  • Se agregó la conversión de tipos a la cadena.

  • Se agregó el parámetro opcional 'allowOverlapping'

  • se corrigió la salida correcta para el caso de "" subcadena vacía.

Esencia
Vitim.us
fuente
55
Repetí esta prueba en Safari 5 y obtuve resultados similares con una cadena pequeña (100b), pero con una cadena más grande (16kb), la expresión regular corrió más rápido para mí. Para una iteración (no 1,000,000), la diferencia fue menos de un milisegundo de todos modos, así que mi voto es para la expresión regular.
arlomedia
2
+1, pero está comprobando substring.lengthen casi todos los bucles, debería considerar while
guardarlo en
1
@ ajax333221 Dios mío, leíste mi mente, hice esta mejora hace unos días e iba a editar mi respuesta jsperf.com/count-string-occurrence-in-string
Vitim.us
44
Encontré su código en uso aquí: success-equation.com/mind_reader.html . Realmente agradable al programador le importaba poner una referencia allí.
Bruno Kim
3
@DanielZuzevich obligará a los tipos a String , en caso de que lo haga occurrences(11,1) //2y aún funcionaría. (Es más rápido hacerlo de esta manera en lugar de buscar tipos y llamar a String () )
Vitim.us
112
function countInstances(string, word) {
   return string.split(word).length - 1;
}
Orbita
fuente
44
Este es un enfoque inseguro / inexacta, por ejemplo: countInstances("isisisisisis", "is") === 0.
Nick Craver
55
@Antal: parece un error en la versión beta anterior de Chrome, funciona después de actualizar a la última versión, aunque todavía me mantendría alejado de este método.
Nick Craver
28
Esto me parece una solución perfectamente válida.
Gregor Schmidt
2
@NickCraver por curiosidad, ¿por qué quieres alejarte de este método? (aparte del error en su navegador beta)
Jonny Lin
66
@JonnyLin crea asignaciones innecesarias que tiras inmediatamente cuando las alternativas no lo hacen, potencialmente muy grandes dependiendo de los datos.
Nick Craver
88

Puedes probar esto:

var theString = "This is a string.";
console.log(theString.split("is").length - 1);

Freeze Ize
fuente
14
¡+1 por la simplicidad y porque, según mis pruebas, esta solución funciona ~ 10 veces más rápido que las otras!
Claudio Holanda
Por ejemplo, tengo dos "es", ¿cómo se obtiene la posición de cada uno?
rapidoodle
Como se discutió en la respuesta de @Orbit, las personas obtienen resultados diferentes en versiones anteriores de Chrome. Quizás sería un poco cauteloso con este método.
mgthomas99
Y también puede usarlo con variables: theString.split(myvar).length - 1que no puede usar con expresiones regulares simples
Steffan
44
Esta es la respuesta de @Orbit tres años después ...
aloisdg se mudó a codidact.com el
33

Mi solución:

var temp = "This is a string.";

function countOcurrences(str, value) {
  var regExp = new RegExp(value, "gi");
  return (str.match(regExp) || []).length;
}

console.log(countOcurrences(temp, 'is'));

Gere
fuente
55
tal vez sería mejor devolver (str.match (regExp) || []). length; De esa manera, ¿no evalúa la expresión regular dos veces?
aikeru
2
también necesita escapar de su cadena o countOcurrences('Hello...','.')==8no 3
Vitim.us
19

Puede usar matchpara definir dicha función:

String.prototype.count = function(search) {
    var m = this.match(new RegExp(search.toString().replace(/(?=[.\\+*?[^\]$(){}\|])/g, "\\"), "g"));
    return m ? m.length:0;
}
Gumbo
fuente
1
Si quisieras que fuera uniforme con la semántica de búsqueda de JS, la línea de retorno sería return m ? m.length:-1;.
Conor O'Brien
Esto es mejor que las otras soluciones de expresiones regulares anteriores, porque causan un error si la cadena para contar las apariciones de es "[" o algo con un significado especial en Regex.
programador
11

La versión no regex:

 var string = 'This is a string',
    searchFor = 'is',
    count = 0,
    pos = string.indexOf(searchFor);

while (pos > -1) {
    ++count;
    pos = string.indexOf(searchFor, ++pos);
}

console.log(count);   // 2

Faraz Kelhini
fuente
1. Es sólo para una sola búsqueda char, demasiado sutil 2. OP incluso pide isocurrencias
vladkras
1
Esta es probablemente la implementación más rápida aquí, pero sería aún más rápido si reemplazara "++ pos" por "pos + = searchFor.length"
hanshenrik
9

String.prototype.Count = function (find) {
    return this.split(find).length - 1;
}

console.log("This is a string.".Count("is"));

Esto devolverá 2.

Fad Seck
fuente
3
Esta es la respuesta de @Orbit seis años después ...
aloisdg se mudó a codidact.com el
8

¡Aquí está la función más rápida!

¿Por qué es más rápido?

  • No comprueba char por char (con 1 excepción)
  • Utiliza un tiempo e incrementa 1 var (el recuento de caracteres var) frente a un bucle para verificar la longitud e incrementar 2 vars (generalmente var i y un var con el recuento de caracteres)
  • Utiliza WAY menos vars
  • ¡No usa expresiones regulares!
  • Utiliza una función (con suerte) altamente optimizada
  • Todas las operaciones son tan combinadas como pueden ser, evitando ralentizaciones debido a múltiples operaciones

    String.prototype.timesCharExist=function(c){var t=0,l=0,c=(c+'')[0];while(l=this.indexOf(c,l)+1)++t;return t};

Aquí hay una versión más lenta y más legible:

    String.prototype.timesCharExist = function ( chr ) {
        var total = 0, last_location = 0, single_char = ( chr + '' )[0];
        while( last_location = this.indexOf( single_char, last_location ) + 1 )
        {
            total = total + 1;
        }
        return total;
    };

Este es más lento debido al contador, los nombres largos de var y el mal uso de 1 var.

Para usarlo, simplemente haz esto:

    'The char "a" only shows up twice'.timesCharExist('a');

Editar: (16/12/2013)

¡NO lo use con Opera 12.16 o anterior! ¡tomará casi 2.5 veces más que la solución regex!

En Chrome, esta solución tomará entre 14ms y 20ms para 1,000,000 de caracteres.

La solución de expresiones regulares tarda 11-14 ms por la misma cantidad.

Usar una función (afuera String.prototype) tomará aproximadamente 10-13 ms.

Aquí está el código utilizado:

    String.prototype.timesCharExist=function(c){var t=0,l=0,c=(c+'')[0];while(l=this.indexOf(c,l)+1)++t;return t};

    var x=Array(100001).join('1234567890');

    console.time('proto');x.timesCharExist('1');console.timeEnd('proto');

    console.time('regex');x.match(/1/g).length;console.timeEnd('regex');

    var timesCharExist=function(x,c){var t=0,l=0,c=(c+'')[0];while(l=x.indexOf(c,l)+1)++t;return t;};

    console.time('func');timesCharExist(x,'1');console.timeEnd('func');

¡El resultado de todas las soluciones debería ser 100,000!

Nota: si desea que esta función cuente más de 1 carácter, cambie dónde está c=(c+'')[0]enc=c+''

Ismael Miguel
fuente
1
¡El prototipo fue UN EJEMPLO! ¡Puedes usar la función como quieras! Incluso puede hacer esto: var timesFunctionExist = function (x, c) {var t = 0, l = 0, c = (c + '') [0]; while (l = x.indexOf (c, l) +1 ) ++ t; devuelve t}); alert (timesCharExist ('El carácter "a" solo aparece dos veces', 'a')) ;! (esto se acelerará un poco más porque no me meteré con prototipos). Si crees que estoy equivocado, ¿por qué no lo muestras antes de tirarme piedras? Demuéstrame que mi función apesta y la aceptaré. Muéstrame un caso de prueba. Y la longitud de los vars tiene influencia en la velocidad. Puedes probarlo.
Ismael Miguel
7

var temp = "This is a string.";
console.log((temp.match(new RegExp("is", "g")) || []).length);

Sunil Garg
fuente
4

Creo que el propósito de la expresión regular es muy diferente indexOf. indexOfsimplemente encuentre la aparición de una determinada cadena mientras que en la expresión regular puede usar comodines, lo [A-Z]que significa que encontrará cualquier carácter en mayúscula en la palabra sin indicar el carácter real.

Ejemplo:

 var index = "This is a string".indexOf("is");
 console.log(index);
 var length = "This is a string".match(/[a-z]/g).length;
 // where [a-z] is a regex wildcard expression thats why its slower
 console.log(length);

Simm
fuente
3

Súper viejo, pero necesitaba hacer algo como esto hoy y solo pensé en verificar SO después. Funciona bastante rápido para mí.

String.prototype.count = function(substr,start,overlap) {
    overlap = overlap || false;
    start = start || 0;

    var count = 0, 
        offset = overlap ? 1 : substr.length;

    while((start = this.indexOf(substr, start) + offset) !== (offset - 1))
        ++count;
    return count;
};
Jason Larke
fuente
3
       var myString = "This is a string.";
        var foundAtPosition = 0;
        var Count = 0;
        while (foundAtPosition != -1)
        {
            foundAtPosition = myString.indexOf("is",foundAtPosition);
            if (foundAtPosition != -1)
            {
                Count++;
                foundAtPosition++;
            }
        }
        document.write("There are " + Count + " occurrences of the word IS");

Consulte: - cuente una subcadena aparece en la cadena para obtener una explicación paso a paso.

Ranju
fuente
3

Sobre la base de @ Vittim.us respuesta anterior. Me gusta el control que me da su método, lo que facilita su extensión, pero necesitaba agregar mayúsculas y minúsculas y limitar las coincidencias a palabras completas con soporte para la puntuación. (por ejemplo, "bañarse" está en "bañarse" pero no en "bañarse")

La expresión regular de puntuación provino de: https://stackoverflow.com/a/25575009/497745 ( ¿Cómo puedo eliminar toda la puntuación de una cadena en JavaScript usando expresiones regulares? )

function keywordOccurrences(string, subString, allowOverlapping, caseInsensitive, wholeWord)
{

    string += "";
    subString += "";
    if (subString.length <= 0) return (string.length + 1); //deal with empty strings

    if(caseInsensitive)
    {            
        string = string.toLowerCase();
        subString = subString.toLowerCase();
    }

    var n = 0,
        pos = 0,
        step = allowOverlapping ? 1 : subString.length,
        stringLength = string.length,
        subStringLength = subString.length;

    while (true)
    {
        pos = string.indexOf(subString, pos);
        if (pos >= 0)
        {
            var matchPos = pos;
            pos += step; //slide forward the position pointer no matter what

            if(wholeWord) //only whole word matches are desired
            {
                if(matchPos > 0) //if the string is not at the very beginning we need to check if the previous character is whitespace
                {                        
                    if(!/[\s\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&\(\)*+,\-.\/:;<=>?@\[\]^_`{|}~]/.test(string[matchPos - 1])) //ignore punctuation
                    {
                        continue; //then this is not a match
                    }
                }

                var matchEnd = matchPos + subStringLength;
                if(matchEnd < stringLength - 1)
                {                        
                    if (!/[\s\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&\(\)*+,\-.\/:;<=>?@\[\]^_`{|}~]/.test(string[matchEnd])) //ignore punctuation
                    {
                        continue; //then this is not a match
                    }
                }
            }

            ++n;                
        } else break;
    }
    return n;
}

Siéntase libre de modificar y refactorizar esta respuesta si detecta errores o mejoras.

Ayo I
fuente
3

Para cualquiera que encuentre este hilo en el futuro, tenga en cuenta que la respuesta aceptada no siempre devolverá el valor correcto si lo generaliza, ya que se ahogará con operadores de expresiones regulares como $y .. Aquí hay una versión mejor, que puede manejar cualquier aguja:

function occurrences (haystack, needle) {
  var _needle = needle
    .replace(/\[/g, '\\[')
    .replace(/\]/g, '\\]')
  return (
    haystack.match(new RegExp('[' + _needle + ']', 'g')) || []
  ).length
}
bcherny
fuente
3

function get_occurrence(varS,string){//Find All Occurrences
        c=(string.split(varS).length - 1);
        return c;
    }
    temp="This is a string.";
    console.log("Total Occurrence is "+get_occurrence("is",temp));

Use get_occurrence (varS, string) para encontrar la ocurrencia de caracteres y cadenas en una cadena.

Rahul Ranjan
fuente
2

Intentalo

<?php 
$str = "33,33,56,89,56,56";
echo substr_count($str, '56');
?>

<script type="text/javascript">
var temp = "33,33,56,89,56,56";
var count = temp.match(/56/g);  
alert(count.length);
</script>
Meghendra S Yadav
fuente
2

Versión simple sin expresiones regulares:

var temp = "This is a string.";

var count = (temp.split('is').length - 1);

alert(count);

Jorge Alberto
fuente
3
Esta es la respuesta de @Orbit seis años después ...
aloisdg se mudó a codidact.com el
2

Nadie verá esto, pero es bueno recuperar las funciones de recursión y flecha de vez en cuando (juego de palabras gloriosamente intencionado)

String.prototype.occurrencesOf = function(s, i) {
 return (n => (n === -1) ? 0 : 1 + this.occurrencesOf(s, n + 1))(this.indexOf(s, (i || 0)));
};
BaseZen
fuente
2

Prueba esto

let allData =  "This is a string.";
let searchString = 'is';
let regularExp = new RegExp(searchString, 'g');
let occurArray = allData.match(regularExp);
let count = (occurArray || []).length;
alert(count); 

Enlace de violín: https://jsfiddle.net/rajaramtt/gn0dtsjc/1/

Raja Rama Mohan Thavalam
fuente
1

Ahora, este es un hilo muy antiguo que he encontrado, pero como muchos han enviado sus respuestas, aquí está el mío con la esperanza de ayudar a alguien con este código simple.

var search_value = "This is a dummy sentence!";
var letter = 'a'; /*Can take any letter, have put in a var if anyone wants to use this variable dynamically*/
letter = letter && "string" === typeof letter ? letter : "";
var count;
for (var i = count = 0; i < search_value.length; count += (search_value[i++] == letter));
console.log(count);

No estoy seguro de si es la solución más rápida, pero la prefiero por simplicidad y por no usar expresiones regulares (¡simplemente no me gusta usarlas!)

Tushar Shukla
fuente
1

Esta función devuelve el número de apariciones de una palabra en el texto.

Tenga en cuenta que usamos toLowerCase para calcular el número de apariciones, independientemente del formato (mayúscula, mayúscula ...) de la palabra y el texto

wordCount(text, word) {
    if (!text || !word) {
      return 0;
    }
    text = text.toLowerCase();
    word = word.toLowerCase();
    return ( text.split( word ).length - 1 );
}
Asakkour Soufiane
fuente
0

Respuesta para Leandro Batista: solo un problema con la expresión regex.

 "use strict";
 var dataFromDB = "testal";
 
  $('input[name="tbInput"]').on("change",function(){
	var charToTest = $(this).val();
	var howManyChars = charToTest.length;
	var nrMatches = 0;
	if(howManyChars !== 0){
		charToTest = charToTest.charAt(0);
		var regexp = new RegExp(charToTest,'gi');
		var arrMatches = dataFromDB.match(regexp);
		nrMatches = arrMatches ? arrMatches.length : 0;
	}
		$('#result').html(nrMatches.toString());

  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
What do you wanna count <input type="text" name="tbInput" value=""><br />
Number of occurences = <span id="result">0</span>
</div>

PhilMaGeo
fuente
0

var countInstances = function(body, target) {
  var globalcounter = 0;
  var concatstring  = '';
  for(var i=0,j=target.length;i<body.length;i++){
    concatstring = body.substring(i-1,j);
    
    if(concatstring === target){
       globalcounter += 1;
       concatstring = '';
    }
  }
  
  
  return globalcounter;
 
};

console.log(   countInstances('abcabc', 'abc')   ); // ==> 2
console.log(   countInstances('ababa', 'aba')   ); // ==> 2
console.log(   countInstances('aaabbb', 'ab')   ); // ==> 1

Kamal
fuente
0

Un poco tarde pero, suponiendo que tengamos la siguiente cadena:

var temp = "This is a string.";

Primero nos dividimos en lo que sea que esté buscando para que coincida, esto devolverá una serie de cadenas.

var array = temp.split("is");

Luego obtenemos su longitud y le restamos 1, ya que los valores predeterminados divididos en una matriz de tamaño 1 y, en consecuencia, aumenta su tamaño cada vez que encuentra una ocurrencia.

var occurrenceCount = array.length - 1;
alert(occurrenceCount); //should output '2'

También puede hacer todo esto en una línea de la siguiente manera:

alert("This is a string.".split("is").length - 1); //should output '2'

Espero que ayude: D

Juan Enrique Segebre
fuente
1
¿Puedo marcar esto como una respuesta duplicada? ¿Tal vez deberías leer todas las respuestas antes de dar la tuya?
Michiel
2
Esta es la respuesta de @Orbit ocho años después ...
aloisdg se mudó a codidact.com el
1
¿Debo eliminar esta respuesta entonces?
Juan Enrique Segebre
0

Esta solución se basa en el .replace()método que acepta un RegEx como primer parámetro y una función como segundo parámetro que podemos usar como cierre para incrementar un contador ...

/**
 * Return the frequency of a substring in a string
 * @param {string} string - The string.
 * @param {string} string - The substring to count.
 * @returns {number} number - The frequency.
 * 
 * @author Drozerah https://gist.github.com/Drozerah/2b8e08d28413d66c3e63d7fce80994ce
 * @see https://stackoverflow.com/a/55670859/9370788
 */
const subStringCounter = (string, subString) => {

    let count = 0
    string.replace(new RegExp(subString, 'gi'), () => count++)
    return count
}

Uso

subStringCounter("foofoofoo", "bar"); //0

subStringCounter("foofoofoo", "foo"); //3
Drozerah
fuente
0

encontré esta publicación.

let str = 'As sly as a fox, as strong as an ox';

let target = 'as'; // let's look for it

let pos = 0;
while (true) {
  let foundPos = str.indexOf(target, pos);
  if (foundPos == -1) break;

  alert( `Found at ${foundPos}` );
  pos = foundPos + 1; // continue the search from the next position
}

El mismo algoritmo puede presentarse más corto:

let str = "As sly as a fox, as strong as an ox";
let target = "as";

let pos = -1;
while ((pos = str.indexOf(target, pos + 1)) != -1) {
  alert( pos );
}
Ashok R
fuente
0

substr_count traducido a Javascript desde php


function substr_count (haystack, needle, offset, length) { 
  // eslint-disable-line camelcase
  //  discuss at: https://locutus.io/php/substr_count/
  // original by: Kevin van Zonneveld (https://kvz.io)
  // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
  // improved by: Brett Zamir (https://brett-zamir.me)
  // improved by: Thomas
  //   example 1: substr_count('Kevin van Zonneveld', 'e')
  //   returns 1: 3
  //   example 2: substr_count('Kevin van Zonneveld', 'K', 1)
  //   returns 2: 0
  //   example 3: substr_count('Kevin van Zonneveld', 'Z', 0, 10)
  //   returns 3: false

  var cnt = 0

  haystack += ''
  needle += ''
  if (isNaN(offset)) {
    offset = 0
  }
  if (isNaN(length)) {
    length = 0
  }
  if (needle.length === 0) {
    return false
  }
  offset--

  while ((offset = haystack.indexOf(needle, offset + 1)) !== -1) {
    if (length > 0 && (offset + needle.length) > length) {
      return false
    }
    cnt++
  }

  return cnt
}

Echa un vistazo a la función substr_count de Locutus Translation of Php

Zachary Horton
fuente
-2

Prueba esto:

function countString(str, search){
    var count=0;
    var index=str.indexOf(search);
    while(index!=-1){
        count++;
        index=str.indexOf(search,index+1);
    }
    return count;
}
Diogo Arenhart
fuente