Порахуйте кількість входжень символу в рядку в JavaScript


525

Мені потрібно порахувати кількість входів символу в рядку.

Наприклад, припустимо, що мій рядок містить:

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

Я хочу знайти кількість ,символів кома , що становить 3. І кількість окремих рядків після розбиття по комах, що дорівнює 4.

Мені також потрібно перевірити, що кожен з рядків, тобто str1 або str2 або str3 або str4, не повинен перевищувати, скажімо, 15 символів.

Відповіді:


765

Я оновив цю відповідь. Мені подобається ідея використовувати сірник краще, але це повільніше:

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

Використовуйте буквений регулярний вираз, якщо ви заздалегідь знаєте, що шукаєте, якщо ні, то не можете використовувати RegExpконструктор і передавати gпрапор як аргумент.

matchповертається nullбез результатів, таким чином|| []

Оригінальна відповідь, яку я зробив у 2009 році, наведена нижче. Він створює масив без потреби, але використовувати розділення швидше (станом на вересень 2014 року). Я амбівалентний, якби мені справді була потрібна швидкість, не виникало б сумніву, що я буду використовувати спліт, але я вважаю за краще використовувати матч.

Стара відповідь (з 2009 року):

Якщо ви шукаєте коми:

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

Якщо ви шукаєте вул

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

Як у відповіді @ Lo, так і в моєму дурному тесті jsperf випереджає швидкість, принаймні в Chrome, але знову ж таки створення додаткового масиву просто не здається розумним.


8
Тест показує, що Firefox набагато швидше, ніж будь-який інший браузер при розбитті. jsperf.com/count-the-number-of-occurances-in-string
vsync

4
Так, я щойно перевірив jsperf vsync, і регулярний вираз був повільнішим у Chrome, Firefox та IE. 68%, 100% та 14% відповідно. У мене i7 2600.
Мосс

56
Мені дуже не подобається ідея використовувати регулярний вираз, тому що "вам це більше подобається". Регекси мають своє призначення, але, як правило, для простого нерегексивного рішення це кращий вибір. Також зауважте, що обидва способи створюють масив, тому це також не привід використовувати регулярний вираз.
Джаспер

4
Мені це подобається краще в цьому випадку з причини. Розбиття рядка на масив, щоб отримати ряд випадків, - це спосіб отримати цю інформацію. Розбиття масиву відбувається лише швидше через деталі реалізації, що може змінитись, тоді як отримання кількості збігів - поліпшення читабельності, намір очевидний і не створює та заповнює невикористану структуру даних.
Бьорн

30
split () - це основний інструмент у javascript, концептуально простий, а підрахунок розбитків дає чіткий намір і є повністю читабельним.
bradw2k

217

Існує як мінімум чотири способи. Найкращий варіант, який також повинен бути найшвидшим - приєднавшись до рідного двигуна RegEx -, розміщується вгорі. jsperf.com наразі не працює, інакше я б надав вам статистику.

Оновлення . Будь ласка, знайдіть тут тести на ефективністьта запускайте їх самі, щоб сприяти результатам роботи. Специфіка результатів буде наведена пізніше.

1.

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

2.

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

розділити не рекомендується. Ресурс голодний. Виділяє нові екземпляри "масиву" для кожного матчу. Не спробуйте цього для файлу> 100 Мб через FileReader. Ви можете легко спостерігати за точним використанням ресурсів, використовуючи опцію профіля 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.

пошук одного персонажа

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

Оновлення:

5.

відображення та фільтрування елементів, не рекомендується через загальне попереднє розміщення ресурсів, а не використання пітонічних "генераторів"

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

Поділіться: я створив цю суть , наразі 8 методів підрахунку символів, тому ми можемо безпосередньо об'єднатись та поділитися своїми ідеями - просто для розваги та, можливо, деяких цікавих орієнтирів :)

https://gist.github.com/2757250


27
Мені знадобилося трохи часу, щоб зрозуміти, що ||[]робив, але ця відповідь чудова! Для всіх, хто чухає голову, match()повертається, nullякщо не знайдено збігів, і ||[]поверне масив довжиною 0, якщо match()повернення null, тобто length()поверне 0, замість помилки типу.
Натан

1
Натане, на мій захист, я детально розробив це до написання коду вище: gist.github.com/2757164 . Я хочу уникати публікацій у блогах з невеликих фрагментів коду, які, однак, дозволили б вам негайно отримати доступ через Google пошук. Суть як сховище фрагментів дуже рідко індексована і менш ідеальна. PS: Я занадто ненавиджу незрозумілі синтаксичні ідіосинкразії.
Лоренц Ло Зауер

2
Ло Зауер, не потрібно захищати себе, код є твердим, і я щось навчився самостійно, з'ясовуючи, як він працює :) Я віддаю перевагу цьому методу над тим, що насправді позначено як відповідь. Не потрібно розбивати рядок, якщо ми не збираємося використовувати результати.
Натан

3
Ваш третій метод (також, на жаль, найшвидший) пропустить будь-яку відповідність за індексом 0 у стозі сіна. Ви можете виправити це, скориставшись циклом do ... while, але: var strsearch = "o", str = "це foo-бар", index = -1, count = -1; виконайте {index = str.indexOf (strsearch, index + 1); рахувати ++; } while (індекс! = -1); кол
Август

1
Досить встановити старт index = -2, але велике спасибі @ Август
Лоренц Ло Зауер

18

Додайте цю функцію до прототипу жала:

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

використання:

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

про що "stringsstringstrings".count("str")?
Тоскан

12

Швидкий пошук Google отримав це (від 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;
}

Використовуйте його так:

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

4
помилка на *char ( SyntaxError: nothing to repeat)

1
аргумент повинен бути регулярним виразом. Тож якщо ви хочете порахувати 's, вам потрібно надіслати' [* ] '
Gerard ONeill

8

Просто скористайтеся розділенням, щоб дізнатися кількість входів символу в рядку.

mainStr.split(',').length // дає 4, що є числом рядків після розщеплення за допомогою роздільника кома

mainStr.split(',').length - 1 // дає 3, що є числом коми


Тут в основному потрібна відповідь. Я шокований ще ніхто не вказував.
Рохіт Гупта

7

Ось подібне рішення, але воно використовує Array.prototype.reduce

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

Як було сказано, String.prototype.splitпрацює набагато швидше, ніж String.prototype.replace.


6

Я виявив, що найкращим підходом до пошуку символу в дуже великій рядку (наприклад, 1 000 000 символів, наприклад) є використання replace()методу.

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

Ви можете побачити ще один набір JSPerf для тестування цього методу разом з іншими методами пошуку символу в рядку.


Очевидно, що якщо ваш код якимось чином повторює понад мільйон символів 500000 разів на секунду, мій процесор працює принаймні 100 ГГц (за умови, що немає SIMD; навіть тоді це було б принаймні 40 ГГц). Тому я не вірю, що цей орієнтир є правильним.
мій займенник monicareinstate

5

Ви також можете відпочити рядок і працювати з ним, як масив елементів, використовуючи

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

console.log(commas);

Або

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

console.log(commas);


1
Друга зручна, дякую!
AlexGera

4

Я зробив невелике поліпшення щодо прийнятої відповіді, вона дозволяє перевірити відповідність регістру / регістру, що не враховує регістр, і це метод, приєднаний до рядкового об'єкта:

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

lit це рядок для пошуку (наприклад, 'ex'), а cis - нечутливість до регістру, дефолт до false, це дозволить вибрати вибір невідчутливих регістрів збігів.


Для пошуку рядка 'I love StackOverflow.com'малої літери 'o'ви використовуєте:

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

amount_of_osбуло б дорівнює 2.


Якби ми знову шукали ту саму рядок, використовуючи невідчутну до регістру відповідність, ви використовуєте:

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

Цього разу amount_of_osце дорівнюватиме 3, оскільки капітал Oіз рядка включається до пошуку.


4

ок, інший з regexp - мабуть, не швидкий, але короткий і легше читабельний, ніж інші, в моєму випадку просто '_'для підрахунку

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

просто видаліть усе, що не виглядає як ваш char, але це не виглядає добре з рядком як вхідним


4

Виступ Спліт проти 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

Найпростіший спосіб я дізнався ...

Приклад-

str = 'mississippi';

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

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

лаконічний! Дякую!
LeOn - Han Li

3

Я працював над невеликим проектом, який потребував лічильника підрядків. Пошук неправильних фраз не дав мені результатів, проте після написання власної реалізації я натрапив на це питання. У будь-якому випадку, ось мій шлях, це, мабуть, повільніше, ніж більшість тут, але може бути корисним комусь:

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/

Будь ласка, дайте мені знати, якщо ви вважаєте, що ця реалізація не працює або не відповідає деяким стандартам! :)

ОНОВЛЕННЯ Ви можете замінити:

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

З:

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

Цікаво читайте, обговорюючи вище: http://www.erichynds.com/blog/javascript-length-property-is-a-stored-value


1
Так, і це працювало б для підрядків, а не лише для підрядків. Однак вам потрібно додати параметри до функції :)
Nico

2

Якщо ви використовуєте lodash, метод _.countBy зробить це так:

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

Цей метод також працює з масивом:

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

2

Ось моє рішення. Багато розв’язання вже розміщено переді мною. Але я люблю ділитися тут своїм поглядом.

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

Тут ви знайдете мою відповідь


2

Здається, найшвидший метод здійснюється через оператор індексу:

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

Або як прототип функції:

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

Далі використовується регулярний вираз для перевірки довжини. testex гарантує, що у вас немає 16 або більше послідовних символів без коми. Якщо він пройде тест, то він переходить до розбиття рядка. підрахунок коми так само простий, як підрахунок жетонів мінус один.

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 (бажанийCharecter) .length-1

Приклад:

var str = "привіт, як живе"; var len = str.split ("h"). довжина-1; дасть підрахунок 2 для символу "h" у верхньому рядку;


1

Я використовую Node.js v.6.0.0, і найшвидший - той, який має індекс (3-й метод у відповіді Ло Зауера).

Другий:

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


1

Ось такий майже такий же швидкий, як методи розщеплення та заміни, які є крихітними швидше, ніж метод регулярного виразів (у хромі).

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

1

Я просто зробив дуже швидкий і брудний тест на repl.it за допомогою Node v7.4. Для одного символу стандарт для циклу є найшвидшим:

Деякі коди :

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

Результати декількох циклів :

 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

І є:

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

Працює і з цілими числами!


0

Моє рішення:

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

Це не працюватиме як String.prototype.matchповернення nullбез збігів. Це означає, що немає посилання на об'єкт з lengthатрибутом. Іншими словами:String.prototype.match.call('willnotwork', /yesitwill/) === null
Лоренц Ло Зауер

0

П'ятий метод у відповіді Лео Соерса провалюється, якщо персонаж знаходиться на початку рядка. напр

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

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

дасть 2 замість 3, тому що функція фільтра Булева дає помилку за 0.

Інші можливі функції фільтра:

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

0

Я знаю, що це може бути старим питанням, але у мене є просте рішення для початківців низького рівня в JavaScript.

Як початківець, я міг зрозуміти лише деякі рішення цього питання, тому я використав дві вкладені петлі FOR, щоб перевірити кожен символ проти кожного другого символу в рядку, збільшуючи змінну підрахунку для кожного символу, що виявився рівним цьому символу.

Я створив новий порожній об’єкт, де кожен ключ властивості є символом, а значення - скільки разів кожен символ з'явився в рядку (count).

Приклад функції: -

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

Я вірю, що ви знайдете наведене нижче рішення як дуже коротке, дуже швидке, здатне працювати з дуже довгими рядками, здатне підтримувати пошук декількох символів, підтвердження помилок та вміти обробляти пошук порожніх рядків.

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

Приклад використання:

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

Вищевказаний код виправляє головну помилку в продуктивності у Jakub Wawszczyk, що код продовжує шукати відповідність навіть після того, як indexOf каже, що його немає, і сама його версія не працює, тому що він забув надати параметри вводу функції.


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 ви можете використовувати вищевказаний код, щоб отримати характер символу в рядку.



0

Функція приймає рядковий рядок як параметр і рахує появу кожного унікального символу в рядку. У результаті виходить пара ключових значень для кожного символу.

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;

} 

Ви забули другу частину запитання: "Мені також потрібно перевірити, що кожен з рядків, тобто str1 або str2 або str3 або str4, не повинен перевищувати, скажімо, 15 символів."
Максим Лауной
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.