Cuente el número de ocurrencias de un carácter en una cadena en Javascript


526

Necesito contar el número de ocurrencias de un personaje en una cadena.

Por ejemplo, supongamos que mi cadena contiene:

var mainStr = "str1,str2,str3,str4";

Quiero encontrar el recuento del ,carácter de coma , que es 3. Y el recuento de cadenas individuales después de la división a lo largo de la coma, que es 4.

También necesito validar que cada una de las cadenas, es decir, str1 o str2 o str3 o str4 no debe exceder, digamos, 15 caracteres.

Respuestas:


766

He actualizado esta respuesta. Me gusta más la idea de usar un partido, pero es más lento:

console.log(("str1,str2,str3,str4".match(/,/g) || []).length); //logs 3

console.log(("str1,str2,str3,str4".match(new RegExp("str", "g")) || []).length); //logs 4

jsfiddle

Use un literal de expresión regular si sabe de antemano lo que está buscando, si no puede usar el RegExpconstructor y pase la gbandera como argumento.

matchvuelve nullsin resultados por lo tanto el|| []

La respuesta original que hice en 2009 está abajo. Crea una matriz innecesariamente, pero el uso de una división es más rápido (a partir de septiembre de 2014). Soy ambivalente, si realmente necesitara la velocidad, no habría duda de que usaría un split, pero preferiría usar match.

Antigua respuesta (de 2009):

Si estás buscando las comas:

(mainStr.split(",").length - 1) //3

Si buscas el str

(mainStr.split("str").length - 1) //4

Tanto en la respuesta de @ Lo como en mi propia tonta prueba de jsperf, la división se adelanta en velocidad, al menos en Chrome, pero una vez más, crear la matriz adicional simplemente no parece cuerdo.


8
La prueba muestra que Firefox es mucho más rápido que cualquier otro navegador cuando se divide. jsperf.com/count-the-number-of-occurances-in-string
vsync

44
Uh, acabo de probar jsperf de vsync y la expresión regular fue más lenta en Chrome, Firefox e IE. 68%, 100% y 14% respectivamente. Tengo un i7 2600.
Moss

57
Realmente no me gusta la idea de usar una expresión regular porque "te gusta más". Las expresiones regulares tienen su propósito, pero generalmente cuando hay una solución simple que no es una expresión regular, es una mejor opción. También tenga en cuenta que ambos métodos crean una matriz, por lo que tampoco es una razón para usar una expresión regular.
Jasper

44
Me gusta más en este caso por una razón. Dividir una cadena en una matriz para obtener una serie de ocurrencias es una forma aproximada de obtener esa información. Dividir una matriz es solo más rápido debido a los detalles de implementación, algo que puede cambiar, mientras que obtener el número de coincidencias es una mejora en la legibilidad, la intención es obvia y no crea y llena una estructura de datos no utilizada.
Bjorn

30
split () es una herramienta básica en javascript, conceptualmente simple, y contar las divisiones da una clara intención y es totalmente legible.
bradw2k

218

Hay al menos cuatro formas. La mejor opción, que también debería ser la más rápida para el motor nativo RegEx, se coloca en la parte superior. jsperf.com está actualmente inactivo, de lo contrario, le proporcionaría estadísticas de rendimiento.

Actualización : por favor, encuentre las pruebas de rendimiento aquí y ejecútelas para contribuir con los resultados de su rendimiento. Los detalles de los resultados se darán más adelante.

1)

 ("this is foo bar".match(/o/g)||[]).length
 //>2

2)

"this is foo bar".split("o").length-1
 //>2

División no recomendada. Recurso hambriento. Asigna nuevas instancias de 'Array' para cada partida. No intente eso para un archivo> 100 MB a través de FileReader. En realidad, puede observar fácilmente el uso EXACTO de los recursos utilizando el generador de perfiles de Chrome opción de .

3)

var stringsearch = "o"
   ,str = "this is foo bar";
for(var count=-1,index=-2; index != -1; count++,index=str.indexOf(stringsearch,index+1) );
 //>count:2

4)

buscando un solo personaje

var stringsearch = "o"
   ,str = "this is foo bar";
for(var i=count=0; i<str.length; count+=+(stringsearch===str[i++]));
 //>count:2

Actualizar:

5)

mapeo y filtrado de elementos, no recomendado debido a la preasignación general de recursos en lugar de usar 'generadores' de Python

var str = "this is foo bar"
str.split('').map( function(e,i){ if(e === 'o') return i;} )
             .filter(Boolean)
//>[9, 10]
[9, 10].length
//>2

Compartir: hice esta esencia , con actualmente 8 métodos de conteo de personajes, para que podamos agrupar y compartir nuestras ideas directamente, solo por diversión y quizás algunos puntos de referencia interesantes :)

https://gist.github.com/2757250


27
Me tomó un tiempo darme cuenta de lo que ||[]estaba haciendo, ¡pero esta respuesta es genial! Para cualquier otra persona que se rasque la cabeza, match()devuelve nullsi no se encuentran coincidencias y ||[]devolverá una matriz de longitud 0 si las match()devuelve null, lo length()que significa que devolverá 0 en lugar de producir un error de tipo.
Nathan

1
Nathan, en mi defensa, lo expliqué antes de escribir el código anterior: gist.github.com/2757164 . Quiero evitar publicaciones en el blog de pequeñas piezas de código, que sin embargo le habrían permitido el acceso instantáneo a través de google-search. Gist como un depósito de fragmentos está indexado muy escasamente y no es ideal. PD: Yo también odio las idiosincrasias sintácticas poco claras.
Lorenz Lo Sauer

2
Lo Sauer, no es necesario que te defiendas, el código es sólido y aprendí algo por mi cuenta al descubrir cómo funcionaba :) Prefiero este método sobre lo que en realidad está marcado como la respuesta. No debería haber necesidad de dividir una cadena si no vamos a usar los resultados.
Nathan

3
Su tercer método (también, desafortunadamente, el más rápido), se perderá cualquier coincidencia en el índice 0 en el pajar. Puede solucionarlo usando un bucle do ... while en su lugar: var strsearch = "o", str = "othis is foo bar", index = -1, count = -1; do {index = str.indexOf (strsearch, index + 1); recuento ++; } while (índice! = -1); recuento
Augustus

1
Es suficiente establecer el inicio index = -2, pero muchas gracias @Augustus
Lorenz Lo Sauer

18

Agregue esta función al prototipo de picadura:

String.prototype.count=function(c) { 
  var result = 0, i = 0;
  for(i;i<this.length;i++)if(this[i]==c)result++;
  return result;
};

uso:

console.log("strings".count("s")); //2

¿qué pasa "stringsstringstrings".count("str")?
Toskan

12

Una búsqueda rápida en Google obtuvo esto (de http://www.codecodex.com/wiki/index.php?title=Count_the_number_of_occurrences_of_a_specific_character_in_a_string#JavaScript )

String.prototype.count=function(s1) { 
    return (this.length - this.replace(new RegExp(s1,"g"), '').length) / s1.length;
}

Úselo así:

test = 'one,two,three,four'
commas = test.count(',') // returns 3

44
error en *char ( SyntaxError: nothing to repeat)

1
El argumento tiene que ser una expresión regular. Entonces, si desea contar 's, debe enviar' [* ] '
Gerard ONeill el

8

Simplemente, use la división para averiguar el número de ocurrencias de un personaje en una cadena.

mainStr.split(',').length // da 4, que es el número de cadenas después de dividir usando la coma delimitador

mainStr.split(',').length - 1 // da 3, que es el recuento de comas


Esta es básicamente la respuesta requerida aquí. Me sorprende que nadie haya señalado todavía.
Rohit Gupta

7

Aquí hay una solución similar, pero utiliza Array.prototype.reduce

function countCharacters(char, string) {
  return string.split('').reduce((acc, ch) => ch === char ? acc + 1: acc, 0)
}

Como se mencionó, String.prototype.splitfunciona mucho más rápido que String.prototype.replace.


6

He descubierto que el mejor enfoque para buscar un carácter en una cadena muy grande (que tiene 1 000 000 caracteres, por ejemplo) es utilizar el replace()método.

window.count_replace = function (str, schar) {
    return str.length - str.replace(RegExp(schar), '').length;
};

Puede ver otra suite JSPerf para probar este método junto con otros métodos para encontrar un carácter en una cadena.


Es obvio que si su código de alguna manera itera más de un millón de caracteres 500000 veces por segundo, mi CPU está funcionando al menos a 100 GHz (suponiendo que no haya SIMD; incluso entonces sería de al menos 40 GHz). Por lo tanto, no creo que este punto de referencia sea correcto.
mi pronombre es monicareinstate el

5

También puede descansar su cadena y trabajar con ella como una matriz de elementos usando

const mainStr = 'str1,str2,str3,str4';
const commas = [...mainStr].filter(l => l === ',').length;

console.log(commas);

O

const mainStr = 'str1,str2,str3,str4';
const commas = [...mainStr].reduce((a, c) => c === ',' ? ++a : a, 0);

console.log(commas);


1
El segundo es útil, ¡gracias!
AlexGera

4

Hice una pequeña mejora en la respuesta aceptada, permite verificar con una coincidencia entre mayúsculas y minúsculas y es un método adjunto al objeto de cadena:

String.prototype.count = function(lit, cis) {
    var m = this.toString().match(new RegExp(lit, ((cis) ? "gi" : "g")));
    return (m != null) ? m.length : 0;
}

lit es la cadena a buscar (como 'ex'), y cis es insensible a mayúsculas y minúsculas, por defecto permitirá elegir coincidencias entre mayúsculas y minúsculas.


Para buscar en la cadena 'I love StackOverflow.com'la letra minúscula 'o', usaría:

var amount_of_os = 'I love StackOverflow.com'.count('o');

amount_of_ossería igual a 2.


Si tuviéramos que buscar la misma cadena nuevamente usando una coincidencia entre mayúsculas y minúsculas, usaría:

var amount_of_os = 'I love StackOverflow.com'.count('o', true);

Esta vez, amount_of_ossería igual a 3, ya que el capital Ode la cadena se incluye en la búsqueda.


4

ok, otro con regexp, probablemente no rápido, pero corto y mejor legible que otros, en mi caso solo '_'para contar

key.replace(/[^_]/g,'').length

simplemente elimine todo lo que no se parece a su char pero no se ve bien con una cadena como entrada


4

Rendimiento de Split vs RegExp

var i = 0;

var split_start = new Date().getTime();
while (i < 30000) {
  "1234,453,123,324".split(",").length -1;
  i++;
}
var split_end = new Date().getTime();
var split_time = split_end - split_start;


i= 0;
var reg_start = new Date().getTime();
while (i < 30000) {
  ("1234,453,123,324".match(/,/g) || []).length;
  i++;
}
var reg_end = new Date().getTime();
var reg_time = reg_end - reg_start;

alert ('Split Execution time: ' + split_time + "\n" + 'RegExp Execution time: ' + reg_time + "\n");


4

La forma más fácil que descubrí ...

Ejemplo-

str = 'mississippi';

function find_occurences(str, char_to_count){
    return str.split(char_to_count).length - 1;
}

find_occurences(str, 'i') //outputs 4

¡conciso! ¡Gracias!
LeOn - Han Li

3

Estaba trabajando en un pequeño proyecto que requería un contador de subcadenas. La búsqueda de frases incorrectas no me dio resultados, sin embargo, después de escribir mi propia implementación, me topé con esta pregunta. De todos modos, aquí está mi camino, probablemente sea más lento que la mayoría aquí, pero podría ser útil para alguien:

function count_letters() {
var counter = 0;

for (var i = 0; i < input.length; i++) {
    var index_of_sub = input.indexOf(input_letter, i);

    if (index_of_sub > -1) {
        counter++;
        i = index_of_sub;
    }
}

http://jsfiddle.net/5ZzHt/1/

¡Avíseme si encuentra que esta implementación falla o no sigue algunos estándares! :)

ACTUALIZACIÓN Es posible que desee sustituir:

    for (var i = 0; i < input.length; i++) {

Con:

for (var i = 0, input_length = input.length; i < input_length; i++) {

Lectura interesante sobre lo anterior: http://www.erichynds.com/blog/javascript-length-property-is-a-stored-value


1
Sí, y funcionaría para subcadenas, no solo subcars. Sin embargo, debe agregar los parámetros a la función :)
Nico

2

Si está usando lodash, el método _.countBy hará esto:

_.countBy("abcda")['a'] //2

Este método también funciona con una matriz:

_.countBy(['ab', 'cd', 'ab'])['ab'] //2

2

Aquí está mi solución. Mucha solución ya publicada antes que yo. Pero me encanta compartir mi punto de vista aquí.

const mainStr = 'str1,str2,str3,str4';

const commaAndStringCounter = (str) => {
  const commas = [...str].filter(letter => letter === ',').length;
  const numOfStr = str.split(',').length;

  return `Commas: ${commas}, String: ${numOfStr}`;
}

// Run the code
console.log(commaAndStringCounter(mainStr)); // Output: Commas: 3, String: 4

Aquí encontrarás mi REPL


2

El método más rápido parece ser a través del operador de índice:

function charOccurances (str, char)
{
  for (var c = 0, i = 0, len = str.length; i < len; ++i)
  {
    if (str[i] == char)
    {
      ++c;
    }
  }
  return c;
}

console.log( charOccurances('example/path/script.js', '/') ); // 2

O como una función prototipo:

String.prototype.charOccurances = function (char)
{
  for (var c = 0, i = 0, len = this.length; i < len; ++i)
  {
    if (this[i] == char)
    {
      ++c;
    }
  }
  return c;
}

console.log( 'example/path/script.js'.charOccurances('/') ); // 2


1

Lo siguiente usa una expresión regular para probar la longitud. testex asegura que no tiene 16 o más caracteres consecutivos sin coma. Si pasa la prueba, procede a dividir la cadena. contar las comas es tan simple como contar las fichas menos una.

var mainStr = "str1,str2,str3,str4";
var testregex = /([^,]{16,})/g;
if (testregex.test(mainStr)) {
  alert("values must be separated by commas and each may not exceed 15 characters");
} else {
  var strs = mainStr.split(',');
  alert("mainStr contains " + strs.length + " substrings separated by commas.");
  alert("mainStr contains " + (strs.length-1) + " commas.");
}

1
s = 'dir/dir/dir/dir/'
for(i=l=0;i<s.length;i++)
if(s[i] == '/')
l++

1

¿Qué pasa con string.split (nedCharecter) .length-1

Ejemplo:

var str = "hola como es la vida"; var len = str.split ("h"). longitud-1; dará el recuento 2 para el carácter "h" en la cadena anterior;


1

Estoy usando Node.js v.6.0.0 y el más rápido es el que tiene índice (el tercer método en la respuesta de Lo Sauer).

El segundo es:

function count(s, c) {
  var n = 0;
  for (let x of s) {
    if (x == c)
      n++;
  }
  return n;
}


1

Aquí hay uno casi tan rápido como los métodos de división y reemplazo, que son un poco más rápidos que el método regex (en cromo).

var num = 0;
for (ch of "str1,str2,str3,str4")
{
    if (ch === ',') num++;
}

1

Acabo de hacer una prueba muy rápida y sucia en repl.it usando Node v7.4. Para un solo carácter, el estándar para el bucle es el más rápido:

Algún código :

// winner!
function charCount1(s, c) {
    let count = 0;
    c = c.charAt(0); // we save some time here
    for(let i = 0; i < s.length; ++i) {
        if(c === s.charAt(i)) {
            ++count;
        }
    }
    return count;
}

function charCount2(s, c) {
    return (s.match(new RegExp(c[0], 'g')) || []).length;
}

function charCount3(s, c) {
    let count = 0;
    for(ch of s) {
        if(c === ch) {
            ++count;
        }
    }
    return count;
}

function perfIt() {
    const s = 'Hello, World!';
    const c = 'o';

    console.time('charCount1');
    for(let i = 0; i < 10000; i++) {
        charCount1(s, c);
    }
    console.timeEnd('charCount1');

    console.time('charCount2');
    for(let i = 0; i < 10000; i++) {
        charCount2(s, c);
    }
    console.timeEnd('charCount2');

    console.time('charCount3');
    for(let i = 0; i < 10000; i++) {
        charCount2(s, c);
    }
    console.timeEnd('charCount3');
}

Resultados de algunas ejecuciones :

 perfIt()
charCount1: 3.843ms
charCount2: 11.614ms
charCount3: 11.470ms
=> undefined
   perfIt()
charCount1: 3.006ms
charCount2: 8.193ms
charCount3: 7.941ms
=> undefined
   perfIt()
charCount1: 2.539ms
charCount2: 7.496ms
charCount3: 7.601ms
=> undefined
   perfIt()
charCount1: 2.654ms
charCount2: 7.540ms
charCount3: 7.424ms
=> undefined
   perfIt()
charCount1: 2.950ms
charCount2: 9.445ms
charCount3: 8.589ms

1

Y ahí está:

function character_count(string, char, ptr = 0, count = 0) {
    while (ptr = string.indexOf(char, ptr) + 1) {count ++}
    return count
}

Funciona con enteros también!


0

Mi solución:

function countOcurrences(str, value){
   var regExp = new RegExp(value, "gi");
   return str.match(regExp) ? str.match(regExp).length : 0;  
}

Esto no funcionará como String.prototype.matchdevoluciones nullsin coincidencias. Eso significa que no hay referencia a un objeto con un lengthatributo. En otras palabras:String.prototype.match.call('willnotwork', /yesitwill/) === null
Lorenz Lo Sauer

0

El quinto método en la respuesta de Leo Sauers falla, si el personaje está al comienzo de la cadena. p.ej

var needle ='A',
  haystack = 'AbcAbcAbc';

haystack.split('').map( function(e,i){ if(e === needle) return i;} )
  .filter(Boolean).length;

dará 2 en lugar de 3, porque la función de filtro Boolean da falso para 0.

Otra posible función de filtro:

haystack.split('').map(function (e, i) {
  if (e === needle) return i;
}).filter(function (item) {
  return !isNaN(item);
}).length;

0

Sé que esta podría ser una vieja pregunta, pero tengo una solución simple para principiantes de bajo nivel en JavaScript.

Como principiante, solo podía entender algunas de las soluciones a esta pregunta, por lo que utilicé dos bucles FOR anidados para comparar cada carácter con cualquier otro carácter en la cadena, incrementando un recuento variable de para cada carácter encontrado que sea igual a ese carácter.

Creé un nuevo objeto en blanco donde cada clave de propiedad es un carácter y el valor es cuántas veces apareció cada carácter en la cadena (cuenta).

Función de ejemplo: -

function countAllCharacters(str) {
  var obj = {};
  if(str.length!==0){
    for(i=0;i<str.length;i++){
      var count = 0;
      for(j=0;j<str.length;j++){
        if(str[i] === str[j]){
          count++;
        }
      }
      if(!obj.hasOwnProperty(str[i])){
        obj[str[i]] = count;
      }
    }
  }
  return obj;
}

0

Creo que encontrará la solución a continuación que es muy corta, muy rápida, capaz de trabajar con cadenas muy largas, capaz de admitir búsquedas de caracteres múltiples, a prueba de errores y capaz de manejar búsquedas de cadenas vacías.

function substring_count(source_str, search_str, index) {
    source_str += "", search_str += "";
    var count = -1, index_inc = Math.max(search_str.length, 1);
    index = (+index || 0) - index_inc;
    do {
        ++count;
        index = source_str.indexOf(search_str, index + index_inc);
    } while (~index);
    return count;
}

Ejemplo de uso:

console.log(substring_count("Lorem ipsum dolar un sit amet.", "m "))

function substring_count(source_str, search_str, index) {
    source_str += "", search_str += "";
    var count = -1, index_inc = Math.max(search_str.length, 1);
    index = (+index || 0) - index_inc;
    do {
        ++count;
        index = source_str.indexOf(search_str, index + index_inc);
    } while (~index);
    return count;
}

El código anterior corrige el error de rendimiento principal en Jakub Wawszczyk que el código sigue buscando una coincidencia incluso después de que indexOf dice que no hay ninguno y su versión en sí no funciona porque olvidó dar los parámetros de entrada de la función.


0
var a = "acvbasbb";
var b= {};
for (let i=0;i<a.length;i++){
    if((a.match(new RegExp(a[i], "g"))).length > 1){
        b[a[i]]=(a.match(new RegExp(a[i], "g"))).length;
    }
}
console.log(b);

En javascript, puede usar el código anterior para obtener la aparición de un carácter en una cadena.


0

Mi solución con ramda js:

const testString = 'somestringtotest'

const countLetters = R.compose(
  R.map(R.length),
  R.groupBy(R.identity),
  R.split('')
)

countLetters(testString)

Enlace a REPL.


0

La función toma string str como parámetro y cuenta la aparición de cada carácter único en la cadena. El resultado viene en pares clave-valor para cada personaje.

var charFoundMap = {};//object defined
    for (var i = 0; i < str.length; i++) {

       if(!charFoundMap[ str[i] ])  {
        charFoundMap[ str[i] ]=1;
       } 
       else
       charFoundMap[ str[i] ] +=1;
       //if object does not contain this 
    }
    return charFoundMap;

} 

Olvidó la segunda parte de la pregunta: "También necesito validar que cada una de las cadenas, es decir, str1 o str2 o str3 o str4 no debe exceder, digamos, 15 caracteres".
Maxime Launois
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.