Compter le nombre d'occurrences d'un caractère dans une chaîne en Javascript

526

J'ai besoin de compter le nombre d'occurrences d'un caractère dans une chaîne.

Par exemple, supposons que ma chaîne contienne:

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

Je veux trouver le nombre de ,caractères de virgule , qui est 3. Et le nombre de chaînes individuelles après la division le long de la virgule, qui est de 4.

Je dois également valider que chacune des chaînes, à savoir str1 ou str2 ou str3 ou str4, ne doit pas dépasser, disons, 15 caractères.

Cœur
la source

Réponses:

766

J'ai mis à jour cette réponse. J'aime l'idée d'utiliser un match mieux, mais c'est plus lent:

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

Utilisez un littéral d'expression régulière si vous savez ce que vous recherchez à l'avance, sinon vous pouvez utiliser le RegExpconstructeur et passez l' gindicateur comme argument.

matchretourne nullsans résultat ainsi la|| []

La réponse originale que j'ai faite en 2009 est ci-dessous. Il crée un tableau inutilement, mais l' utilisation d'un fractionnement est plus rapide (à partir de septembre 2014). Je suis ambivalent, si j'avais vraiment besoin de la vitesse, il n'y aurait aucun doute que j'utiliserais un split, mais je préférerais utiliser match.

Ancienne réponse (de 2009):

Si vous recherchez les virgules:

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

Si vous cherchez le str

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

Tant dans la réponse de @ Lo que dans mon propre split de test jsperf idiot vient en avant en vitesse, au moins dans Chrome, mais encore une fois, la création du tableau supplémentaire ne semble pas raisonnable.

Bjorn
la source
8
le test montre que Firefox est beaucoup plus rapide que tout autre navigateur lors du fractionnement. jsperf.com/count-the-number-of-occurances-in-string
vsync
4
Euh, je viens de tester le jsperf de vsync et l'expression régulière était plus lente dans Chrome, Firefox et IE. 68%, 100% et 14% respectivement. J'ai un i7 2600.
Moss
57
Je n'aime vraiment pas l'idée d'utiliser une expression régulière parce que "vous l'aimez mieux". Les regex ont leur raison d'être, mais généralement, lorsqu'il existe une solution simple non regex, c'est un meilleur choix. Notez également que les deux méthodes créent un tableau, ce n'est donc pas une raison pour utiliser une expression régulière non plus.
Jasper
4
Je l'aime mieux dans ce cas pour une raison. Fractionner une chaîne en un tableau pour obtenir un certain nombre d'occurrences est un moyen détourné d'obtenir ces informations. Le fractionnement d'un tableau n'est plus rapide qu'en raison des détails d'implémentation, ce qui peut changer, tandis que l'obtention du nombre de correspondances est une amélioration de la lisibilité, l'intention est évidente et ne crée pas et ne remplit pas une structure de données inutilisée.
Bjorn
30
split () est un outil de base en javascript, conceptuellement simple, et le comptage des divisions donne une intention claire et est totalement lisible.
bradw2k
218

Il y a au moins quatre façons. La meilleure option, qui devrait également être la plus rapide grâce au moteur RegEx natif, est placée en haut. jsperf.com est actuellement en panne, sinon je vous fournirais des statistiques de performances.

Mise à jour : Veuillez trouver les tests de performances ici et exécutez-les vous-même, afin de contribuer à vos résultats de performances. Les détails des résultats seront donnés ultérieurement.

1.

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

2.

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

split non recommandé. Affamé de ressources. Alloue de nouvelles instances de «tableau» pour chaque correspondance. N'essayez pas cela pour un fichier> 100 Mo via FileReader. Vous pouvez en fait facilement observer l'utilisation des ressources EXACT en utilisant l' option de profileur de 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.

recherche d'un seul caractère

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

Mise à jour:

5.

mappage et filtrage des éléments, déconseillés en raison de la préallocation globale des ressources plutôt que d'utiliser des «générateurs» pythoniens

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

Partager: J'ai créé cet aperçu , avec actuellement 8 méthodes de comptage de caractères, afin que nous puissions directement mettre en commun et partager nos idées - juste pour le plaisir, et peut-être quelques repères intéressants :)

https://gist.github.com/2757250

Lorenz Lo Sauer
la source
27
Il m'a fallu un peu de temps pour réaliser ce qui se ||[]passait mais cette réponse est géniale! Pour toute autre personne se grattant la tête, match()retourne nullsi aucune correspondance n'est trouvée et ||[]retournera un tableau de longueur 0 si le match()retourne null, ce length()qui signifie retournera 0 au lieu de produire une erreur de type.
Nathan
1
Nathan, pour ma défense, j'ai développé cela avant d'écrire le code ci-dessus: gist.github.com/2757164 . Je veux éviter les articles de blog de petits morceaux de code, qui cependant vous auraient permis un accès instantané via la recherche google. Gist en tant que référentiel d'extraits est très peu indexé et loin d'être idéal. PS: Je déteste moi aussi les idiosyncrasies syntaxiques peu claires.
Lorenz Lo Sauer
2
Lo Sauer, pas besoin de se défendre, le code est solide et j'ai appris quelque chose par moi-même en découvrant comment cela fonctionnait :) Je préfère cette méthode à ce qui est réellement marqué comme réponse. Il ne devrait pas être nécessaire de diviser une chaîne si nous n'utilisons pas les résultats.
Nathan
3
Votre troisième méthode (aussi, malheureusement, la plus rapide), ratera tout match à l'index 0 dans la botte de foin. Vous pouvez le corriger en utilisant une boucle do ... while à la place: var strsearch = "o", str = "othis is foo bar", index = -1, count = -1; do {index = str.indexOf (strsearch, index + 1); count ++; } while (index! = -1); compter
Augustus
1
Il suffit de fixer le départ index = -2, mais merci beaucoup @Augustus
Lorenz Lo Sauer
18

Ajoutez cette fonction au prototype sting:

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

usage:

console.log("strings".count("s")); //2
Philippe Boissonneault
la source
qu'en est-il "stringsstringstrings".count("str")?
Toskan
12

Une recherche rapide sur Google a obtenu ceci (depuis 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;
}

Utilisez-le comme ceci:

test = 'one,two,three,four'
commas = test.count(',') // returns 3
user253751
la source
4
erreur sur *char ( SyntaxError: nothing to repeat)
1
l'argument doit être une expression régulière. Donc, si vous voulez compter , vous devez envoyer '[* ]'
Gerard ONeill
8

Utilisez simplement le fractionnement pour connaître le nombre d'occurrences d'un caractère dans une chaîne.

mainStr.split(',').length // donne 4 qui est le nombre de chaînes après la séparation en utilisant la virgule délimiteur

mainStr.split(',').length - 1 // donne 3 qui est le nombre de virgules

Pranjal Successena
la source
C'est essentiellement la réponse requise ici. Je suis choqué, personne ne l'a encore signalé.
Rohit Gupta
7

Voici une solution similaire, mais elle utilise Array.prototype.reduce

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

Comme cela a été mentionné, String.prototype.splitfonctionne beaucoup plus rapidement que String.prototype.replace.

uladzimir
la source
6

J'ai trouvé que la meilleure approche pour rechercher un caractère dans une très grande chaîne (c'est-à-dire 1 000 000 de caractères par exemple) est d'utiliser la replace()méthode.

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

Vous pouvez voir encore une autre suite JSPerf pour tester cette méthode ainsi que d'autres méthodes de recherche d'un caractère dans une chaîne.

Valera Rozuvan
la source
Il est évident que si votre code itère en quelque sorte plus d'un million de caractères 500000 fois par seconde, mon processeur fonctionne à au moins 100 GHz (en supposant qu'il n'y ait pas de SIMD; même alors, il serait d'au moins 40 GHz). Je ne pense donc pas que ce critère soit correct.
mon pronom est monicareinstate
5

Vous pouvez également reposer votre chaîne et travailler avec elle comme un tableau d'éléments en utilisant

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
la source
1
Le second est pratique, merci!
AlexGera
4

J'ai apporté une légère amélioration à la réponse acceptée, cela permet de vérifier avec une correspondance sensible à la casse / insensible à la casse, et est une méthode attachée à l'objet chaîne:

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

lit est la chaîne à rechercher (comme 'ex'), et cis est insensible à la casse, par défaut à false, il permettra de choisir des correspondances insensibles à la casse.


Pour rechercher la chaîne 'I love StackOverflow.com'de la lettre minuscule 'o', vous utiliseriez:

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

amount_of_os serait égal à 2 .


Si nous devions rechercher à nouveau la même chaîne en utilisant une correspondance insensible à la casse, vous utiliseriez:

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

Cette fois, amount_of_osserait égal à 3, car le capital Ode la chaîne est inclus dans la recherche.

Dendromaniac
la source
4

ok, un autre avec regexp - probablement pas rapide, mais court et mieux lisible que les autres, dans mon cas juste '_'pour compter

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

il suffit de supprimer tout ce qui ne ressemble pas à votre caractère mais il n'a pas l'air sympa avec une chaîne en entrée

halfbit
la source
4

Performances 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
la source
4

Le moyen le plus simple que j'ai découvert ...

Exemple-

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
la source
concis! Merci!
LeOn - Han Li
3

Je travaillais sur un petit projet qui nécessitait un compteur de sous-chaînes. La recherche des phrases erronées ne m'a donné aucun résultat, mais après avoir écrit ma propre implémentation, je suis tombé sur cette question. Quoi qu'il en soit, voici mon chemin, il est probablement plus lent que la plupart ici, mais pourrait être utile à quelqu'un:

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/

Veuillez me faire savoir si vous trouvez que cette implémentation échoue ou ne respecte pas certaines normes! :)

MISE À JOUR Vous pouvez remplacer:

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

Avec:

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

Lecture intéressante discutant de ce qui précède: http://www.erichynds.com/blog/javascript-length-property-is-a-stored-value

Jakub Wawszczyk
la source
1
Oui, et cela fonctionnerait pour la sous-chaîne, pas seulement pour les sous-caractères. Cependant, vous devez ajouter les paramètres à la fonction :)
Nico
2

Si vous utilisez lodash, la méthode _.countBy fera cela:

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

Cette méthode fonctionne également avec le tableau:

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

Voici ma solution. Beaucoup de solutions déjà affichées devant moi. Mais j'aime partager mon point de vue ici.

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

Ici vous trouvez mon REPL

Md. Jamal Uddin
la source
2

La méthode la plus rapide semble être via l'opérateur d'index:

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 comme fonction prototype:

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
la source
1

Ce qui suit utilise une expression régulière pour tester la longueur. testex garantit que vous ne disposez pas de 16 caractères non virgule consécutifs ou plus. S'il réussit le test, il procède à la division de la chaîne. compter les virgules est aussi simple que compter les jetons moins un.

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

Qu'en est-il de string.split (desireCharecter) .length-1

Exemple:

var str = "hellow how is life"; var len = str.split ("h"). length-1; donnera le compte 2 pour le caractère "h" dans la chaîne ci-dessus;

user2296195
la source
1

J'utilise Node.js v.6.0.0 et le plus rapide est celui avec index (la 3ème méthode dans la réponse de Lo Sauer).

Le second est:

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

Marc K.
la source
1

En voici une presque aussi rapide que les méthodes de fractionnement et de remplacement, qui sont un peu plus rapides que la méthode regex (en chrome).

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

Je viens de faire un test très rapide et sale sur repl.it en utilisant Node v7.4. Pour un seul caractère, la norme de boucle est la plus rapide:

Du code :

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

Résultats de quelques essais :

 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
la source
1

Et voici:

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

Fonctionne aussi avec des entiers!

Damion Dooley
la source
0

Ma solution:

function countOcurrences(str, value){
   var regExp = new RegExp(value, "gi");
   return str.match(regExp) ? str.match(regExp).length : 0;  
}
Gere
la source
Cela ne fonctionnera pas comme String.prototype.matchretours nullsans correspondances. Cela signifie aucune référence à un objet avec un lengthattribut. En d'autres termes:String.prototype.match.call('willnotwork', /yesitwill/) === null
Lorenz Lo Sauer
0

La cinquième méthode dans la réponse de Leo Sauers échoue, si le caractère se trouve au début de la chaîne. par exemple

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

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

donnera 2 au lieu de 3, car la fonction de filtre booléen donne false pour 0.

Autre fonction de filtrage possible:

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

Je sais que cela pourrait être une vieille question, mais j'ai une solution simple pour les débutants de bas niveau en JavaScript.

En tant que débutant, je ne pouvais comprendre que certaines des solutions à cette question, j'ai donc utilisé deux boucles FOR imbriquées pour comparer chaque caractère par rapport à tous les autres caractères de la chaîne, en incrémentant un nombre variable de pour chaque caractère trouvé égal à ce caractère.

J'ai créé un nouvel objet vide où chaque clé de propriété est un caractère et la valeur est le nombre de fois que chaque caractère est apparu dans la chaîne (count).

Exemple de fonction: -

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;
}
Vicomte Wathika
la source
0

Je pense que vous trouverez la solution ci-dessous très courte, très rapide, capable de travailler avec de très longues chaînes, capable de prendre en charge plusieurs recherches de caractères, résistante aux erreurs et capable de gérer des recherches de chaînes vides.

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

Exemple d'utilisation:

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

Le code ci-dessus corrige le bogue de performance majeur dans Jakub Wawszczyk que le code continue à rechercher une correspondance même après que indexOf indique qu'il n'y en a pas et que sa version elle-même ne fonctionne pas car il a oublié de donner les paramètres d'entrée de la fonction.

Jack Giffin
la source
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, vous pouvez utiliser le code ci-dessus pour obtenir l'occurrence d'un caractère dans une chaîne.

Nitine.
la source
0

Ma solution avec ramda js:

const testString = 'somestringtotest'

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

countLetters(testString)

Lien vers REPL.

Michal
la source
0

La fonction prend la chaîne str comme paramètre et compte l'occurrence de chaque caractère unique dans la chaîne. Le résultat est fourni par paire clé-valeur pour chaque caractère.

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
la source
Vous avez oublié la deuxième partie de la question: "Je dois également valider que chacune des chaînes, à savoir str1 ou str2 ou str3 ou str4, ne doit pas dépasser, disons, 15 caractères."
Maxime Launois