Подсчитайте количество вхождений символа в строку в Javascript

526

Мне нужно посчитать количество вхождений символа в строку.

Например, предположим, что моя строка содержит:

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

Я хочу найти количество запятой , символов , которое равно 3. И количество отдельных строк после разделения через запятую, которое равно 4.

Мне также нужно проверить, что каждая из строк, т.е. str1 или str2 или str3 или str4, не должна превышать, скажем, 15 символов.

Кер
источник

Ответы:

766

Я обновил этот ответ. Мне нравится идея использовать матч лучше, но он медленнее:

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, но опять же создание дополнительного массива просто не кажется нормальным.

Bjorn
источник
8
Тест показывает, что Firefox намного быстрее любого другого браузера при разделении. jsperf.com/count-the-number-of-occurances-in-string
vsync
4
Я только что проверил jsperf vsync, и регулярные выражения были медленнее в Chrome, Firefox и IE. 68%, 100% и 14% соответственно. У меня i7 2600.
Мосс,
57
Мне действительно не нравится идея использования регулярных выражений, потому что «вам это нравится больше». Регулярные выражения имеют свое предназначение, но обычно, когда есть простое решение без регулярных выражений, это лучший выбор. Также обратите внимание, что оба метода создают массив, так что это также не является причиной для использования регулярных выражений.
Джаспер
4
Мне нравится это лучше в этом случае по причине. Разделение строки в массив для получения количества вхождений - это способ получения этой информации. Разделение массива происходит быстрее только из-за деталей реализации, что может измениться, в то время как получение количества совпадений является улучшением читабельности, цель очевидна и не создает и не заполняет неиспользуемую структуру данных.
Бьорн
30
split () - базовый инструмент в javascript, концептуально простой, и подсчет разбиений дает четкое намерение и полностью читабелен.
Bradw2k
218

Есть как минимум четыре способа. Лучший вариант, который также должен быть самым быстрым - благодаря собственному движку RegEx, - расположен сверху. jsperf.com в настоящее время не работает, в противном случае я бы предоставил вам статистику производительности.

Обновление : пожалуйста, найдите здесь тесты производительностии сами запустите их, чтобы улучшить результаты своей работы. Специфика результатов будет дана позже.

1.

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

2.

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

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

сопоставление и фильтрация элементов, не рекомендуется из-за общего предварительного распределения ресурсов, а не из-за использования 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

Поделиться: Я сделал эту суть , с в настоящее время 8 методов подсчета символов, так что мы можем напрямую объединять и делиться своими идеями - просто для удовольствия, и, возможно, некоторые интересные тесты :)

https://gist.github.com/2757250

Лоренц Ло Зауэр
источник
27
Мне потребовалось немного времени, чтобы понять, что ||[]происходит, но этот ответ великолепен! Для любого, кто почесал голову, match()возвращается, nullесли совпадений не найдено, и ||[]возвращает 0, если match()возвращается null, то есть length()будет возвращать 0 вместо выдачи ошибки типа.
Натан
1
Натан, в свою защиту, я разработал это до написания кода выше: gist.github.com/2757164 . Я хочу избегать постов в блогах с небольшими фрагментами кода, которые, однако, позволили бы вам получить мгновенный доступ через google-поиск. Gist как хранилище фрагментов очень редко индексируется и не идеален. PS: я тоже ненавижу неясные синтаксические особенности.
Лоренц Ло Зауэр
2
Ло Зауэр, не нужно защищать себя, код надежный, и я кое-что узнал сам, выяснив, как это работает :) Я предпочитаю этот метод, а не тот, который фактически отмечен как ответ. Там не должно быть необходимости разбивать строку, если мы не собираемся использовать результаты.
Натан
3
Ваш третий метод (также, к сожалению, самый быстрый) пропустит любое совпадение с индексом 0 в стоге сена. Вы можете исправить это, используя вместо этого цикл do ... while: var strsearch = "o", str = "othis is foo bar", index = -1, count = -1; do {index = str.indexOf (strsearch, index + 1); подсчитывать ++; } while (index! = -1); граф
Август
1
Достаточно установить начало index = -2, но большое спасибо @Augustus
Лоренц Ло Зауэр
18

Добавьте эту функцию в прототип sting:

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_occurferences_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
user253751
источник
4
ошибка на *char ( SyntaxError: nothing to repeat)
1
аргумент должен быть регулярным выражением. Так что если вы хотите посчитать , вам нужно отправить «[*
Джерард 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.

Uladzimir
источник
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строка из строки включается в поиск.

Dendromaniac
источник
4

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

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

просто удалите все, что не похоже на ваш символ, но это не выглядит хорошо со строкой в ​​качестве ввода

halfbit
источник
4

Производительность Split против 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
Анкур Хорайвал
источник
краткий! Спасибо!
Леон - Хан Ли
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
Geng Jiawen
источник
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

Здесь вы найдете мой REPL

Джамал Уддин
источник
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

zoran404
источник
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++
WLF
источник
1

Как насчет string.split (требуемый характер) .length-1

Пример:

var str = "привет как жизнь"; var len = str.split ("h"). length-1; даст счет 2 для символа "h" в приведенной выше строке;

user2296195
источник
1

Я использую Node.js v.6.0.0, и самый быстрый из них - с индексом (третий метод в ответе Ло Сауэра).

Второе:

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

Марк К.
источник
1

Вот один почти такой же быстрый, как методы split и replace, которые чуть-чуть быстрее, чем метод регулярных выражений (в chrome).

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
NuSkooler
источник
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, потому что функция фильтра Boolean дает false для 0.

Другая возможная функция фильтра:

haystack.split('').map(function (e, i) {
  if (e === needle) return i;
}).filter(function (item) {
  return !isNaN(item);
}).length;
saltimbokka
источник
0

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

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

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

Пример функции: -

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

Мое решение с помощью ramda js:

const testString = 'somestringtotest'

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

countLetters(testString)

Ссылка на REPL.

Michal
источник
0

Функция принимает строку str в качестве параметра и подсчитывает вхождение каждого уникального символа в строку. Результат приходит в паре ключ-значение для каждого символа.

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 символов».
Максим Лонуа