Javascript'te bir dizedeki bir karakterin yineleme sayısını sayma


525

Bir dize bir karakterin oluşum sayısını saymak gerekir.

Örneğin, dizemin şunları içerdiğini varsayalım:

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

Ben 3 olan virgül ,karakter sayısını bulmak istiyorum . Ve 4 olan virgül boyunca bölünmüş sonra bireysel dizelerin sayısı.

Ayrıca, str1 veya str2 veya str3 veya str4 dizelerinin her birinin, örneğin 15 karakteri geçmemesi gerektiğini doğrulamam gerekiyor.

Yanıtlar:


765

Bu cevabı güncelledim. Bir maçı daha iyi kullanma fikrini seviyorum, ancak daha yavaş:

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

Önceden ne aradığınızı biliyorsanız, normal bir ifade değişmezi kullanın, eğer değilse yapıcıyı kullanabilir RegExpve gbayrağı bağımsız değişken olarak geçirebilirsiniz .

matchnullsonuç vermeden döner|| []

2009'da verdiğim orijinal cevap aşağıda. Gereksiz yere bir dizi oluşturur, ancak bölünme kullanmak daha hızlıdır (Eylül 2014 itibariyle). Kararsızım, gerçekten hıza ihtiyacım olsaydı, bir split kullanacağım diye bir soru olmayacaktı, ama maçı kullanmayı tercih ederim.

Eski cevap (2009'dan itibaren):

Virgül arıyorsanız:

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

Eğer str arıyorsanız

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

Hem @ Lo'nun cevabında hem de kendi aptal jsperf test bölümümde hız, en azından Chrome'da geliyor, ancak yine ekstra dizi oluşturmak aklı başında görünmüyor.


8
Test, Firefox'un bölünürken diğer tarayıcılardan çok daha hızlı olduğunu gösteriyor. jsperf.com/count-the-number-of-occurances-in-string
vsync

4
Uh, az önce vsync'in jsperf testini yaptım ve normal ifade Chrome, Firefox ve IE'de daha yavaştı . Sırasıyla% 68,% 100 ve% 14. Bir i7 2600'üm var.
Moss

56
Normal ifadeyi kullanma fikrinden gerçekten hoşlanmıyorum çünkü "daha iyi seviyorsun". Normal ifadelerin amacı vardır, ancak genellikle normal normal olmayan bir çözüm olduğunda daha iyi bir seçimdir. Ayrıca her iki yöntemin de bir dizi oluşturduğunu unutmayın, bu nedenle normal ifadeyi de kullanmanın bir nedeni değildir.
Jasper

4
Bu durumda bir sebepten daha iyi hoşlanıyorum . Bir dizeyi bir dizi tekrar elde etmek için bir diziye bölmek, bu bilgiyi almanın bir yoludur. Bir dizinin bölünmesi yalnızca uygulama ayrıntıları, değişebilen bir şey nedeniyle daha hızlıdır, ancak eşleşme sayısını elde etmek okunabilirlikte bir gelişmedir, amaç açıktır ve kullanılmayan bir veri yapısı oluşturmaz ve doldurmaz.
Bjorn

30
split (), javascript'te temel olarak basit bir araçtır ve bölmeleri saymak açık bir niyet verir ve tamamen okunabilirdir.
bradw2k

217

En az dört yol var. Yerel RegEx motorunun en hızlı olması gereken en iyi seçenek en üste yerleştirilmiştir. jsperf.com şu anda kapalı, aksi takdirde size performans istatistikleri sunacağım.

Güncelleme : Lütfen,performans sonuçlarınıza katkıda bulunmak için performanstestlerini burada bulunve kendiniz çalıştırın. Sonuçların detayları daha sonra verilecektir.

1.

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

2.

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

split tavsiye edilmez. Kaynak aç. Her maç için yeni 'Dizi' örnekleri atar. FileReader üzerinden 100 MB'tan büyük bir dosya için bunu denemeyin. Chrome'un profil oluşturucu seçeneğini kullanarak EXACT kaynak kullanımını kolayca gözlemleyebilirsiniz .

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.

tek bir karakter aramak

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

Güncelleme:

5.

eleman eşleme ve filtreleme, Pythonian 'jeneratörleri' kullanmak yerine genel kaynak ön yerleşimi nedeniyle önerilmez

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

Paylaş: Bunu yapan özünü doğrudan havuzu ve fikirlerimizi paylaşabilmeleri, karakter-sayma şu anda 8 yöntemlerle, - sadece eğlence için ve belki bazı ilginç kriterler :)

https://gist.github.com/2757250


27
Ne ||[]yaptığının farkına varmak biraz zamanımı aldı ama bu cevap harika! Başka başlarını çizilmeye herkes için match()döner nulleşleşme bulunursa ve eğer ||[]varsa bir 0 boy dizi döndürür match()döner null, anlamı length()bir tür hatası üretmek yerine 0 döndürecektir.
Nathan

1
Nathan, savunmam için, yukarıdaki kodu yazmadan önce bunun üzerinde durdum : gist.github.com/2757164 . Küçük kod parçalarının blog gönderilerinden kaçınmak istiyorum, ancak google-arama yoluyla anında erişime izin verirdim. Snippet deposu olarak öz, çok seyrek olarak endekslenir ve idealden daha azdır. PS: Ben de belirsiz sözdizimsel özdeyişlerden nefret ediyorum.
Lorenz Lo Sauer

2
Lo Sauer, kendinizi savunmaya gerek yok, kod sağlam ve nasıl çalıştığını anlayarak kendi başıma bir şey öğrendim :) Aslında cevap olarak işaretlenmiş olan bu yöntemi tercih ederim. Sonuçları kullanmayacaksak bir dizeyi ayırmaya gerek yoktur.
Nathan

3
Üçüncü yönteminiz (maalesef en hızlı), samanlıkta dizin 0'daki herhangi bir eşleşmeyi kaçırır. Bunun yerine bir do ... while döngüsü kullanarak düzeltebilirsiniz: var strsearch = "o", str = "o foo bar", index = -1, count = -1; do {index = str.indexOf (strsearch, dizin + 1); ++ saymak; } while (dizin! = -1); sayısı
Augustus

1
Başlangıç ​​ayarlamak yeterli index = -2, ancak çok teşekkürler @Augustus
Lorenz Lo Sauer

18

Bu işlevi sokma prototipine ekleyin:

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

kullanımı:

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

Ne hakkında "stringsstringstrings".count("str")?
Toskan

12

Hızlı bir Google araması bunu yaptı ( 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;
}

Şöyle kullanın:

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

4
*char hatası ( SyntaxError: nothing to repeat)

1
argüman düzenli bir ifade olmalıdır. Eğer 's saymak istiyorsanız,' [* ] ' göndermek gerekir
Gerard ONeill

8

Basitçe, dizedeki bir karakterin tekrarlama sayısını bulmak için bölmeyi kullanın.

mainStr.split(',').length // ayırıcı virgül kullanarak bölündükten sonra dize sayısı olan 4 değerini verir

mainStr.split(',').length - 1 verir / virgül sayısı olan 3'ü verir


Burada temel olarak gerekli cevap budur. Henüz kimsenin işaret etmediği için şok oldum.
Rohit Gupta

7

İşte benzer bir çözüm, ancak kullanıyor Array.prototype.reduce

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

Belirtildiği gibi, String.prototype.splitçok daha hızlı çalışır String.prototype.replace.


6

Çok büyük bir dize (örneğin, 000 000 karakter uzunluğunda) bir karakter aramak için en iyi yaklaşım yöntemi kullanmak olduğunu bulduk replace().

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

Bu yöntemi bir dizede bir karakter bulmanın diğer yöntemleriyle birlikte test etmek için başka bir JSPerf paketi görebilirsiniz .


Kodunuz saniyede 500000 kez bir milyondan fazla karakter yineliyorsa, CPU'mun en az 100GHz (SIMD olmadığını varsayarsak; en azından 40GHz olacağı varsayılır) çalıştığı açıktır. Bu yüzden bu kriterin doğru olduğuna inanmıyorum.
zamirim

5

Ayrıca dizenizi dinlendirebilir ve kullanarak bir dizi öğe gibi çalışabilirsiniz.

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

console.log(commas);

Veya

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

console.log(commas);


1
İkincisi kullanışlı, teşekkürler!
AlexGera

4

Kabul edilen cevapta hafif bir iyileştirme yaptım, büyük / küçük harfe duyarlı / büyük / küçük harf duyarsız eşleştirme ile kontrol etmeyi sağlar ve dize nesnesine eklenmiş bir yöntemdir:

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

lit aranacak dizedir ('ex' gibi) ve cis büyük / küçük harf duyarsızdır, varsayılanı yanlıştır, büyük / küçük harf duyarsız eşleşmelerin seçilmesine izin verir.


Dizede 'I love StackOverflow.com'küçük harf aramak için şunu 'o'kullanırsınız:

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

amount_of_oseşit olacaktır 2.


Büyük / küçük harfe duyarlı olmayan eşlemeyi kullanarak aynı dizeyi tekrar arayacak olsaydık, şunu kullanırdınız:

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

Bu kez, dizeden gelen sermaye aramaya dahil edildiğinden amount_of_oseşit olacaktır .3O


4

Tamam, regexp ile başka bir - muhtemelen hızlı değil, ama kısa ve daha iyi okunabilir diğerleri, benim durumumda sadece '_'saymak

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

sadece karakterinize benzemeyen her şeyi kaldırın, ancak girdi olarak bir dize ile hoş görünmüyor


4

Split vs RegExp Performansı

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

Öğrenmenin en kolay yolu ...

Misal-

str = 'mississippi';

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

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

Özlü! Teşekkürler!
LeOn - Han Li

3

Alt dize sayacı gerektiren küçük bir proje üzerinde çalışıyordum. Yanlış ifadeleri aramak bana hiçbir sonuç vermedi, ancak kendi uygulamamı yazdıktan sonra bu soru üzerine tökezledim. Her neyse, işte benim yolum, muhtemelen çoğu burada daha yavaş ama birileri için yararlı olabilir:

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/

Bu uygulamanın başarısız olduğunu veya bazı standartlara uymadığını tespit ederseniz lütfen bize bildirin! :)

GÜNCELLEME Aşağıdakileri değiştirmek isteyebilirsiniz:

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

İle:

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

Yukarıdakileri tartışan ilginç bir okuma: http://www.erichynds.com/blog/javascript-length-property-is-a-stored-value


1
Evet ve yalnızca alt şemalar için değil, alt dize için de işe yarar. Ancak, parametreye işleve eklemeniz gerekir :)
Nico

2

Lodash kullanıyorsanız, _.countBy yöntemi bunu yapar:

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

Bu yöntem dizi ile de çalışır:

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

2

İşte benim çözümüm. Benden önce bir sürü çözüm gönderildi. Ama görüşümü burada paylaşmayı seviyorum.

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

Burada REPL'imi bul


2

En hızlı yöntem dizin operatörü aracılığıyla görünüyor:

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

Veya bir prototip işlevi olarak:

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

Aşağıda, uzunluğu test etmek için normal bir ifade kullanılmaktadır. testex, birbirini takip eden 16 veya daha fazla virgül içermeyen karakterin olmamasını sağlar. Testi geçerse, dizeyi ayırmaya devam eder. virgül saymak, belirteçleri eksi bir saymak kadar basittir.

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

String.split (desiredCharecter) .length-1 hakkında ne

Misal:

var str = "hayat nasıl?" var len = str.split ("h"). uzunluk-1; yukarıdaki dizgideki "h" karakteri için 2 sayısını verecektir;


1

Node.js v.6.0.0 kullanıyorum ve en hızlı indeksi olan (Lo Sauer'ın cevabındaki 3. yöntem).

İkincisi:

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


1

İşte regex yönteminden (kromda) biraz daha hızlı olan split ve replace yöntemleri kadar hızlı.

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

1

Ben sadece repl.it üzerinde çok hızlı ve kirli bir test yaptım Düğüm v7.4 kullanarak. Tek bir karakter için, döngü standardı en hızlıdır:

Bazı kodlar :

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

Birkaç çalıştırmadan sonuçlar :

 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

Ve orada:

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

Tamsayılarla da çalışır!


0

Çözümüm:

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

Bu, eşleşme olmadan String.prototype.matchgeri dönüş olarak çalışmaz null. Bu, lengthözniteliği olan bir nesneye başvuru olmadığı anlamına gelir . Başka bir deyişle:String.prototype.match.call('willnotwork', /yesitwill/) === null
Lorenz Lo Sauer

0

Karakter dizenin başındaysa, Leo Sauers yanıtındaki beşinci yöntem başarısız olur. Örneğin

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

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

3 yerine 2 verir, çünkü Boolean filtre fonksiyonu 0 için false değerini verir.

Diğer olası filtre fonksiyonu:

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

0

Bunun eski bir soru olabileceğini biliyorum, ancak JavaScript'te düşük seviyeli yeni başlayanlar için basit bir çözümüm var.

Bir acemi olarak, sadece iki iç içe kullanıldığı bu yüzden bu soruya çözümlerden bazılarını anlayabileceği İÇİN döngüler bir arttırılarak dizede her karakterin karşı her bir karakteri kontrol etmek sayımı her karakter için değişkenini bu karaktere eşit buldum.

Her özellik anahtarının bir karakter olduğu ve değerin her karakterin dizede (sayım) kaç kez göründüğü yeni bir boş nesne oluşturdum.

Örnek fonksiyon: -

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

Aşağıdaki çözümün çok kısa, çok hızlı, çok uzun dizelerle çalışabileceğini, birden fazla karakter aramasını destekleyebildiğini, hataya dayanıklı olduğunu ve boş dize aramaları yapabildiğini düşünüyorum.

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

Örnek kullanım:

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

Yukarıdaki kod, Jakub Wawszczyk'teki büyük performans hatasını düzelterek, indexOf'un hiçbiri olmadığını ve işlev giriş parametrelerini vermeyi unuttuğu için sürümünün çalışmadığını söyledikten sonra bile bir eşleşme arar.


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

Javascript bir dizede bir karakter oluşumunu elde etmek için yukarıdaki kodu kullanabilirsiniz.


0

Ramda JS ile benim çözüm:

const testString = 'somestringtotest'

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

countLetters(testString)

REPL bağlantısı.


0

İşlev string dizesini parametre olarak alır ve dizgideki her benzersiz karakterin oluşumunu sayar. Sonuç, her karakter için anahtar / değer çiftinde gelir.

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;

} 

Sorunun ikinci bölümünü unuttunuz: "Ayrıca, dizelerin her birinin, yani str1 veya str2 veya str3 veya str4'ün, örneğin 15 karakteri geçmemesi gerektiğini de doğrulamam gerekiyor."
Maxime Launois
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.