Conta il numero di occorrenze di un personaggio in una stringa in Javascript


526

Devo contare il numero di occorrenze di un personaggio in una stringa.

Ad esempio, supponiamo che la mia stringa contenga:

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

Voglio trovare il conteggio del ,carattere virgola , che è 3. E il conteggio delle singole stringhe dopo la divisione lungo la virgola, che è 4.

Devo anche confermare che ciascuna delle stringhe, ovvero str1 o str2 o str3 o str4, non deve superare, diciamo, 15 caratteri.

Risposte:


766

Ho aggiornato questa risposta. Mi piace l'idea di usare meglio una partita, ma è più 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

Usa un'espressione regolare letterale se sai in anticipo cosa stai cercando, in caso contrario puoi usare il RegExpcostruttore e passare la gbandiera come argomento.

matchrestituisce nullsenza risultati quindi il|| []

La risposta originale che ho fatto nel 2009 è di seguito. Crea un array inutilmente, ma l' utilizzo di uno split è più veloce (a settembre 2014). Sono ambivalente, se avessi davvero bisogno della velocità non ci sarebbe dubbio che avrei usato una divisione, ma preferirei usare la corrispondenza.

Vecchia risposta (dal 2009):

Se stai cercando le virgole:

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

Se stai cercando lo str

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

Sia nella risposta di @ Lo che nella mia stupida divisione di test jsperf procede in velocità, almeno in Chrome, ma creare di nuovo l'array extra non sembra sano.


8
il test mostra che Firefox è molto più veloce di qualsiasi altro browser durante la divisione. jsperf.com/count-the-number-of-occurances-in-string
vsync

4
Ho appena provato jsperf di vsync e il regex era più lento in Chrome, Firefox e IE. 68%, 100% e 14% rispettivamente. Ho un i7 2600.
Moss,

57
Non mi piace l'idea di usare un regex perché "ti piace di più". I regex hanno il loro scopo, ma generalmente quando c'è una soluzione non regex semplice è una scelta migliore. Si noti inoltre che entrambi i metodi creano un array, quindi non è nemmeno una ragione per usare una regex.
Jasper,

4
Mi piace di più in questo caso per un motivo. Dividere una stringa in un array per ottenere un numero di occorrenze è un modo per ottenere tali informazioni. La suddivisione di un array è solo più rapida a causa dei dettagli di implementazione, qualcosa che può cambiare, mentre ottenere il numero di corrispondenze è un miglioramento della leggibilità, l'intento è ovvio e non crea e riempie una struttura di dati inutilizzata.
Bjorn,

30
split () è uno strumento di base in javascript, concettualmente semplice, e il conteggio delle divisioni dà chiare intenzioni ed è totalmente leggibile.
Bradk2k,

218

Ci sono almeno quattro modi. L'opzione migliore, che dovrebbe anche essere la più veloce, il motore nativo RegEx, è posizionata in alto. jsperf.com è attualmente inattivo, altrimenti ti fornirei le statistiche sulle prestazioni.

Aggiornamento : trova qui i test delle prestazionied eseguili tu stesso, in modo da contribuire ai risultati delle tue prestazioni. Le specifiche dei risultati verranno fornite in seguito.

1.

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

2.

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

divisione non consigliata. Fame di risorse. Alloca nuove istanze di 'Array' per ogni partita. Non provarlo per un file> 100 MB tramite FileReader. Puoi effettivamente osservare facilmente l'utilizzo delle risorse EXACT usando l' opzione del profiler di 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.

alla ricerca di un singolo personaggio

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

Aggiornare:

5.

mappatura e filtro degli elementi, non raccomandato a causa della sua preallocazione globale delle risorse piuttosto che usando i "generatori" di Pythonian

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

Condividi: ho fatto questa idea , con attualmente 8 metodi di conteggio dei personaggi, in modo da poter raggruppare e condividere direttamente le nostre idee - solo per divertimento e forse alcuni benchmark interessanti :)

https://gist.github.com/2757250


27
Mi ci è voluto un po 'di tempo per capire cosa ||[]stesse facendo, ma questa risposta è fantastica! Per chiunque si gratta la testa, match()ritorna nullse non viene trovata alcuna corrispondenza e ||[]restituisce un array di lunghezza 0 se match()restituisce null, il che significa length()che restituirà 0 invece di produrre un errore di tipo.
Nathan,

1
Nathan, a mia difesa, l'ho approfondito prima di scrivere il codice sopra: gist.github.com/2757164 . Voglio evitare i post sul blog di piccoli pezzi di codice, che tuttavia ti avrebbero consentito l'accesso immediato tramite la ricerca di Google. Gist come repository di snippet è molto scarsamente indicizzato e meno che ideale. PS: anche io odio le idiosincrasie sintattiche poco chiare.
Lorenz Lo Sauer,

2
Lo Sauer, non c'è bisogno di difendersi, il codice è solido e ho imparato qualcosa da solo, scoprendo come ha funzionato :) Preferisco questo metodo rispetto a ciò che è effettivamente contrassegnato come risposta. Non dovrebbe essere necessario dividere una stringa se non useremo i risultati.
Nathan,

3
Il tuo terzo metodo (anche, purtroppo, il più veloce), mancherà qualsiasi partita all'indice 0 nel pagliaio. Puoi risolverlo usando invece un ciclo do ... while: var strsearch = "o", str = "othis is foo bar", indice = -1, count = -1; do {index = str.indexOf (strsearch, indice + 1); contare ++; } while (indice! = -1); conteggio
Augusto

1
Basta iniziare index = -2, ma grazie mille @Augustus
Lorenz Lo Sauer

18

Aggiungi questa funzione al prototipo di sting:

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

utilizzo:

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

che dire "stringsstringstrings".count("str")?
Toskan,

12

Una rapida ricerca su Google ha ottenuto questo (da 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;
}

Usalo in questo modo:

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

4
errore su *char ( SyntaxError: nothing to repeat)

1
l'argomento deve essere un'espressione regolare. Quindi, se vuoi contare , devi inviare '[* ]'
Gerard ONeill

8

Semplicemente, usa la divisione per scoprire il numero di occorrenze di un carattere in una stringa.

mainStr.split(',').length // dà 4 che è il numero di stringhe dopo la divisione usando la virgola delimitatore

mainStr.split(',').length - 1 // dà 3 che è il conteggio della virgola


Questa è sostanzialmente la risposta richiesta qui. Sono scioccato che nessuno abbia ancora sottolineato.
Rohit Gupta,

7

Ecco una soluzione simile, ma utilizza Array.prototype.reduce

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

Come accennato, String.prototype.splitfunziona molto più velocemente di String.prototype.replace.


6

Ho scoperto che l'approccio migliore per cercare un carattere in una stringa molto grande (che è lunga 1 000 000 caratteri, per esempio) è usare il replace()metodo.

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

Puoi vedere ancora un'altra suite JSPerf per testare questo metodo insieme ad altri metodi per trovare un carattere in una stringa.


È ovvio che se il tuo codice in qualche modo scorre oltre un milione di caratteri 500000 volte al secondo, la mia CPU funziona a almeno 100 GHz (supponendo che non sia SIMD; anche allora sarebbe almeno 40 GHz). Quindi non credo che questo benchmark sia corretto.
mio pronome è monicareinstate il

5

Puoi anche riposare la tua stringa e lavorarci come una matrice di elementi 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
Il secondo è utile, grazie!
AlexGera,

4

Ho apportato un leggero miglioramento alla risposta accettata, mi consente di verificare la corrispondenza tra maiuscole / minuscole e maiuscole ed è un metodo collegato all'oggetto stringa:

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

lit è la stringa da cercare (come 'ex'), e cis è insensibilità al maiuscolo / minuscolo, il valore predefinito è false, consentirà la scelta di corrispondenze insensibili al maiuscolo / minuscolo.


Per cercare la stringa 'I love StackOverflow.com'per la lettera minuscola 'o', utilizzare:

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

amount_of_ossarebbe uguale a 2.


Se dovessimo cercare nuovamente la stessa stringa utilizzando la corrispondenza senza distinzione tra maiuscole e minuscole, utilizzare:

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

Questa volta, amount_of_ossarebbe uguale a 3, poiché il capitale Odella stringa viene incluso nella ricerca.


4

ok, un altro con regexp - probabilmente non veloce, ma breve e meglio leggibile di altri, nel mio caso solo '_'per contare

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

basta rimuovere tutto ciò che non assomiglia al tuo carattere ma non sembra carino con una stringa come input


4

Prestazioni di 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

Il modo più semplice che ho scoperto ...

Esempio-

str = 'mississippi';

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

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

conciso! Grazie!
LeOn - Han Li

3

Stavo lavorando a un piccolo progetto che richiedeva un contatore di sottostringa. La ricerca di frasi sbagliate non mi ha fornito risultati, tuttavia dopo aver scritto la mia implementazione mi sono imbattuto in questa domanda. Comunque, ecco la mia strada, probabilmente è più lento della maggior parte qui, ma potrebbe essere utile a qualcuno:

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/

Per favore fatemi sapere se trovate che questa implementazione fallisce o non seguite alcuni standard! :)

AGGIORNAMENTO Potresti voler sostituire:

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

Con:

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

Interessante lettura che discute di quanto sopra: http://www.erichynds.com/blog/javascript-length-property-is-a-stored-value


1
Sì, e funzionerebbe per la sottostringa, non solo per i subchar. Tuttavia, è necessario aggiungere i parametri alla funzione :)
Nico

2

Se stai usando lodash, il metodo _.countBy farà questo:

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

Questo metodo funziona anche con l'array:

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

2

Ecco la mia soluzione Molte soluzioni sono già state pubblicate prima di me. Ma adoro condividere la mia opinione qui.

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

Qui trovi il mio REPL


2

Il metodo più veloce sembra essere tramite l'operatore indice:

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 come funzione 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

Quanto segue utilizza un'espressione regolare per testare la lunghezza. testex ti assicura di non avere 16 o più caratteri consecutivi non virgola. Se supera il test, procede alla divisione della stringa. contare le virgole è semplice come contare i token meno uno.

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

Che dire string.split (wishCharecter) .length-1

Esempio:

var str = "hellow come è la vita"; var len = str.split ("h"). lunghezza-1; darà il conteggio 2 per il carattere "h" nella stringa sopra;


1

Sto usando Node.js v.6.0.0 e il più veloce è quello con indice (il terzo metodo nella risposta di Lo Sauer).

Il secondo è:

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


1

Eccone uno veloce quanto i metodi split e sostituisci, che sono un po 'più veloci del metodo regex (in chrome).

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

1

Ho appena fatto un test molto veloce e sporco su repl.it usando Node v7.4. Per un singolo personaggio, lo standard per loop è il più veloce:

Qualche codice :

// 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');
}

Risultati da alcune corse :

 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

E c'è:

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

Funziona anche con numeri interi!


0

La mia soluzione:

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

Questo non funzionerà come String.prototype.matchritorni nullsenza corrispondenze. Ciò significa che nessun riferimento a un oggetto con un lengthattributo. In altre parole:String.prototype.match.call('willnotwork', /yesitwill/) === null
Lorenz Lo Sauer,

0

Il quinto metodo nella risposta di Leo Sauers fallisce, se il personaggio si trova all'inizio della stringa. per esempio

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

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

darà 2 invece di 3, perché la funzione di filtro Boolean fornisce false per 0.

Altre possibili funzioni di filtro:

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

0

So che questa potrebbe essere una vecchia domanda, ma ho una soluzione semplice per i principianti di basso livello in JavaScript.

Come principiante, ho potuto comprendere solo alcune delle soluzioni a questa domanda, quindi ho usato due cicli FOR annidati per verificare ogni carattere rispetto a ogni altro carattere nella stringa, aumentando un conteggio variabile di per ogni carattere trovato uguale a quel carattere.

Ho creato un nuovo oggetto vuoto in cui ogni chiave di proprietà è un carattere e il valore è quante volte ogni personaggio è apparso nella stringa (conteggio).

Esempio di funzione: -

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

Credo che troverai la soluzione seguente molto breve, molto veloce, in grado di lavorare con stringhe molto lunghe, in grado di supportare ricerche di più caratteri, a prova di errore e in grado di gestire ricerche di stringhe vuote.

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

Esempio di utilizzo:

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

Il codice sopra risolve il principale bug di prestazione in Jakub Wawszczyk che il codice continua a cercare una corrispondenza anche dopo indexOf dice che non ce n'è e la sua versione stessa non funziona perché si è dimenticato di fornire i parametri di input della funzione.


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

In javascript puoi usare il codice sopra per ottenere l'occorrenza di un carattere in una stringa.


0

La mia soluzione con ramda js:

const testString = 'somestringtotest'

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

countLetters(testString)

Link a REPL.


0

La funzione accetta la stringa str come parametro e conta l'occorrenza di ciascun carattere univoco nella stringa. Il risultato arriva in coppia chiave-valore per ogni carattere.

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;

} 

Hai dimenticato la seconda parte della domanda: "Devo anche confermare che ciascuna delle stringhe, ovvero str1 o str2 o str3 o str4, non deve superare, diciamo, 15 caratteri".
Maxime Launois,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.