Contar o número de ocorrências de um caractere em uma string em Javascript

525

Preciso contar o número de ocorrências de um caractere em uma string.

Por exemplo, suponha que minha string contenha:

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

Quero encontrar a contagem de ,caracteres de vírgula , que é 3. E a contagem de cadeias individuais após a divisão ao longo da vírgula, que é 4.

Também preciso validar que cada uma das strings, ou seja, str1 ou str2 ou str3 ou str4 não exceda, digamos, 15 caracteres.

Cœur
fonte

Respostas:

766

Eu atualizei esta resposta. Eu gosto mais da ideia de usar uma partida, mas é mais lenta:

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 uma expressão regular literal se souber o que está pesquisando anteriormente; caso contrário, poderá usar o RegExpconstrutor e passar a gflag como argumento.

matchretorna nullsem resultados, portanto, o|| []

A resposta original que fiz em 2009 está abaixo. Ele cria uma matriz desnecessariamente, mas o uso de uma divisão é mais rápido (em setembro de 2014). Sou ambivalente, se eu realmente precisasse da velocidade, não haveria dúvida de que usaria uma divisão, mas preferiria usar a partida.

Resposta antiga (de 2009):

Se você estiver procurando por vírgulas:

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

Se você está procurando o str

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

Tanto na resposta do @ Lo quanto no meu bobo jsperf, o teste vem à frente em velocidade, pelo menos no Chrome, mas novamente criar a matriz extra não parece sensato.

Bjorn
fonte
8
O teste mostra que o Firefox é muito mais rápido do que qualquer outro navegador durante a divisão. jsperf.com/count-the-number-of-occurances-in-string
vsync
4
Acabei de testar o jsperf do vsync e o regex foi mais lento no Chrome, Firefox e IE. 68%, 100% e 14%, respectivamente. Eu tenho um i7 2600.
Moss
57
Eu realmente não gosto da idéia de usar um regex porque "você gosta mais". Regexes têm seu objetivo, mas geralmente quando há uma solução simples que não seja regex, é uma escolha melhor. Observe também que os dois métodos criam uma matriz, portanto, esse também não é um motivo para usar uma regex.
Jasper
4
Eu gosto mais neste caso por uma razão. Dividir uma sequência em uma matriz para obter várias ocorrências é uma maneira de obter essas informações. A divisão de uma matriz é apenas mais rápida devido aos detalhes da implementação, algo que pode mudar, enquanto a obtenção do número de correspondências é uma melhoria na legibilidade, a intenção é óbvia e não cria e preenche uma estrutura de dados não utilizada.
Bjorn
30
split () é uma ferramenta básica em javascript, conceitualmente simples, e contar as divisões dá uma intenção clara e é totalmente legível.
Bradw2k
217

Existem pelo menos quatro maneiras. A melhor opção, que também deve ser a mais rápida para o mecanismo RegEx nativo -, é colocada no topo. O jsperf.com está desativado no momento, caso contrário, eu forneceria estatísticas de desempenho.

Atualização : encontre aqui os testes de desempenhoe execute-os, para contribuir com seus resultados de desempenho. As especificidades dos resultados serão fornecidas posteriormente.

1

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

2)

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

divisão não recomendada. Com fome de recursos. Aloca novas instâncias de 'Matriz' para cada correspondência. Não tente isso para um arquivo> 100 MB via FileReader. Você pode observar facilmente o uso EXATO dos recursos usando a opção de criação de perfil do Chrome .

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)

procurando um único caractere

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

Atualizar:

5)

mapeamento e filtragem de elementos, não recomendado devido à sua pré-localização geral de recursos, em vez de usar 'geradores' do 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

Compartilhe: Eu criei essa essência , com atualmente 8 métodos de contagem de caracteres, para que possamos compartilhar e compartilhar nossas idéias diretamente - apenas por diversão, e talvez por algumas referências interessantes :)

https://gist.github.com/2757250

Lorenz Lo Sauer
fonte
27
Demorei um pouco para perceber o que ||[]estava fazendo, mas essa resposta é ótima! Para qualquer outra pessoa que coça a cabeça, match()retorna nullse nenhuma correspondência for encontrada e ||[]retornará uma matriz de comprimento 0 se match()retornar null, ou length()seja, retornará 0 em vez de produzir um erro de tipo.
Nathan
1
Nathan, para minha defesa, eu expliquei isso antes de escrever o código acima: gist.github.com/2757164 . Eu quero evitar postagens no blog de pequenos pedaços de código, que, no entanto, permitiriam acesso instantâneo através da pesquisa no Google. O Gist como repositório de trechos é muito pouco indexado e menos do que o ideal. PS: Eu também odeio idiossincrasias sintáticas pouco claras.
Lorenz Lo Sauer
2
Lo Sauer, não há necessidade de se defender, o código é sólido e eu aprendi algo por conta própria, descobrindo como funcionava :) Prefiro esse método ao invés do que é realmente marcado como resposta. Não deve ser necessário dividir uma sequência se não vamos usar os resultados.
Nathan
3
Seu terceiro método (também, infelizmente, o mais rápido), perderá qualquer partida no índice 0 no palheiro. Você pode corrigi-lo usando um loop do ... while: var strsearch = "o", str = "este é o foo bar", índice = -1, count = -1; do {index = str.indexOf (strsearch, index + 1); count ++; } while (índice! = -1); contagem
Augustus
1
Basta dar o começo index = -2, mas muito obrigado @Augustus
Lorenz Lo Sauer
18

Adicione esta função ao protótipo de picada:

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
Philippe Boissonneault
fonte
que tal "stringsstringstrings".count("str")?
Toskan 17/04
12

Uma rápida pesquisa no Google conseguiu isso (em 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;
}

Use-o assim:

test = 'one,two,three,four'
commas = test.count(',') // returns 3
user253751
fonte
4
Erro no *char ( SyntaxError: nothing to repeat)
1
o argumento deve ser uma expressão regular. Portanto, se você quiser contar os números, precisará enviar '[* ]'
Gerard ONeill
8

Simplesmente, use a divisão para descobrir o número de ocorrências de um caractere em uma string.

mainStr.split(',').length // fornece 4, que é o número de cadeias após a divisão usando vírgula delimitadora

mainStr.split(',').length - 1 // fornece 3, que é a contagem de vírgula

Pranjal Successena
fonte
Esta é basicamente a resposta necessária aqui. Estou chocado que ninguém apontou ainda.
Rohit Gupta
7

Aqui está uma solução semelhante, mas ela usa Array.prototype.reduce

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

Como foi mencionado, String.prototype.splitfunciona muito mais rápido que String.prototype.replace.

uladzimir
fonte
6

Descobri que a melhor abordagem para procurar um caractere em uma string muito grande (com 1 000 000 caracteres, por exemplo) é usar o replace()método

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

Você pode ver ainda outro conjunto JSPerf para testar esse método, juntamente com outros métodos para localizar um caractere em uma sequência.

Valera Rozuvan
fonte
É óbvio que, se o seu código, de alguma forma, iterar mais de um milhão de caracteres 500000 vezes por segundo, minha CPU estará executando pelo menos 100 GHz (assumindo que não haja SIMD; mesmo assim, seria pelo menos 40 GHz). Portanto, não acredito que este parâmetro esteja correto.
meu pronome é monicareinstate
5

Você também pode descansar sua string e trabalhar com ela como uma matriz de elementos usando

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

console.log(commas);

Ou

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

console.log(commas);

Yosvel Quintero Arguelles
fonte
1
O segundo é útil, obrigado!
AlexGera
4

Fiz uma pequena melhoria na resposta aceita, ela permite verificar com correspondência sensível a maiúsculas / minúsculas e é um método anexado ao objeto string:

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

lit é a string a ser pesquisada (como 'ex') e cis não faz distinção entre maiúsculas e minúsculas, o padrão é false, permitirá a escolha de correspondências sem distinção entre maiúsculas e minúsculas.


Para pesquisar a string 'I love StackOverflow.com'pela letra minúscula 'o', você usaria:

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

amount_of_os seria igual a 2 .


Se procurássemos a mesma sequência novamente usando a correspondência que não diferencia maiúsculas de minúsculas, você usaria:

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

Dessa vez, amount_of_osseria igual a 3, já que o capital Oda sequência é incluído na pesquisa.

Dendromaniac
fonte
4

ok, outro com regexp - provavelmente não é rápido, mas curto e melhor legível que outros, no meu caso apenas '_'para contar

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

basta remover tudo o que não se parece com o seu caractere, mas não fica bonito com uma string como entrada

halfbit
fonte
4

Desempenho 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");

Clive Paterson
fonte
4

A maneira mais fácil que eu descobri ...

Exemplo-

str = 'mississippi';

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

find_occurences(str, 'i') //outputs 4
Ankur Choraywal
fonte
conciso! Obrigado!
LeOn - Han Li
3

Eu estava trabalhando em um pequeno projeto que exigia um contador de sub-string. A pesquisa de frases erradas não me forneceu resultados; no entanto, depois de escrever minha própria implementação, me deparei com essa pergunta. Enfim, aqui está o meu caminho, provavelmente é mais lento que a maioria aqui, mas pode ser útil para alguém:

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/

Informe-me se você encontrar esta implementação com falha ou se não seguir alguns padrões! :)

ATUALIZAÇÃO Você pode substituir:

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

Com:

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

Leitura interessante discutindo o acima: http://www.erichynds.com/blog/javascript-length-property-is-a-stored-value

Jakub Wawszczyk
fonte
1
Sim, e funcionaria para substring, não apenas subchars. No entanto, você precisa adicionar os parâmetros para a função :)
Nico
2

Se você estiver usando lodash, o método _.countBy fará isso:

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

Este método também trabalha com array:

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

Aqui está a minha solução. Muitas soluções já postadas antes de mim. Mas eu amo compartilhar minha opinião aqui.

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

Aqui você encontra o meu REPL

Mestre Jamal Uddin
fonte
2

O método mais rápido parece ser através do 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

Ou como uma função de protótipo:

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

zoran404
fonte
1

A seguir, uma expressão regular para testar o comprimento. O testex garante que você não tenha 16 ou mais caracteres consecutivos sem vírgula. Se passar no teste, ele continuará dividindo a sequência. contar as vírgulas é tão simples quanto contar os tokens menos um.

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.");
}
Jonathan Fingland
fonte
1
s = 'dir/dir/dir/dir/'
for(i=l=0;i<s.length;i++)
if(s[i] == '/')
l++
wlf
fonte
1

E quanto a string.split (gráfico desejado) .length-1

Exemplo:

var str = "abaixo, como está a vida"; var len = str.split ("h"). length-1; dará a contagem 2 para o caractere "h" na string acima;

user2296195
fonte
1

Estou usando o Node.js. v.6.0.0 e o mais rápido é o que possui um índice (o terceiro método na resposta de Lo Sauer).

O segundo é:

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

Marc K.
fonte
1

Aqui está um quase tão rápido quanto os métodos de divisão e substituição, que são um pouco mais rápidos que o método regex (no chrome).

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

Acabei de fazer um teste muito rápido e sujo no repl.it usando o Node v7.4. Para um único caractere, o loop for padrão é o mais rápido:

Algum 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 algumas execuções :

 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
NuSkooler
fonte
1

E aqui está:

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

Funciona com números inteiros também!

Damion Dooley
fonte
0

Minha solução:

function countOcurrences(str, value){
   var regExp = new RegExp(value, "gi");
   return str.match(regExp) ? str.match(regExp).length : 0;  
}
Gere
fonte
Isso não funcionará como String.prototype.matchretornos nullsem correspondências. Isso significa que não há referência a um objeto com um lengthatributo. Em outras palavras:String.prototype.match.call('willnotwork', /yesitwill/) === null
Lorenz Lo Sauer
0

O quinto método na resposta de Leo Sauers falha, se o caractere estiver no início da string. por exemplo

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

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

dará 2 em vez de 3, porque a função de filtro Boolean fornece false para 0.

Outra função de filtro possível:

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

Sei que essa pode ser uma pergunta antiga, mas tenho uma solução simples para iniciantes de baixo nível em JavaScript.

Como iniciante, eu só conseguia entender algumas das soluções para essa pergunta, então usei dois loops FOR aninhados para verificar cada caractere contra qualquer outro caractere na cadeia, incrementando uma variável de contagem para cada caractere encontrado igual a esse caractere.

Criei um novo objeto em branco em que cada chave de propriedade é um caractere e o valor é quantas vezes cada caractere apareceu na sequência (contagem).

Exemplo de função: -

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;
}
Visconde Wathika
fonte
0

Acredito que você achará a solução abaixo muito curta, muito rápida, capaz de trabalhar com seqüências muito longas, capaz de oferecer suporte a pesquisas de vários caracteres, à prova de erros e capaz de lidar com pesquisas de cadeias vazias.

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

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

O código acima corrige o principal bug de desempenho do Jakub Wawszczyk, que o código continua procurando uma correspondência mesmo depois que o indexOf diz que não há nenhum e sua versão em si não está funcionando porque ele esqueceu de fornecer os parâmetros de entrada da função.

Jack Giffin
fonte
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);

Em javascript, você pode usar o código acima para obter a ocorrência de um caractere em uma string.

Nitin.
fonte
0

Minha solução com ramda js:

const testString = 'somestringtotest'

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

countLetters(testString)

Link para REPL.

Michal
fonte
0

A função usa a string str como parâmetro e conta a ocorrência de cada caractere exclusivo na string. O resultado vem no par de chave-valor para cada caractere.

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;

} 
Pratibha Singh
fonte
Você esqueceu a segunda parte da pergunta: "Também preciso validar que cada uma das strings, por exemplo, str1 ou str2 ou str3 ou str4 não deve exceder, digamos, 15 caracteres".
Maxime Launois