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

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

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

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

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

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

39 ответов

Решение

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

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

Есть как минимум четыре способа. Лучший вариант, который также должен быть самым быстрым - благодаря собственному движку 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

Добавьте эту функцию в прототип 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

Просто используйте разделение, чтобы узнать количество вхождений символа в строку.

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

mainStr.split(',').length - 1 // дает 3, которое является запятой

Удивительно, что за 13 лет этот ответ не появился. Интуитивно кажется, что это должно быть быстрее:

      const s = "The quick brown fox jumps over the lazy dog.";
const oCount = s.length - s.replaceAll('o', '').length;

Если в строке только два типа символов, то это еще быстрее:

      
const s = "001101001";
const oneCount = s.replaceAll('0', '').length;

Быстрый поиск Google получил это (от http://www.codecodex.com/wiki/index.php?title=Count_the_number_of_occurrences_of_a_specific_character_in_a_string)

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

Вы также можете отдохнуть свою строку и работать с ней как массив элементов, используя

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

Вот аналогичное решение, но оно использует уменьшить

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

Как уже упоминалось, расколоть гораздо быстрее, чем заменить.

Если вы используете lodash, метод _.countBy сделает это:

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

Этот метод также работает с массивом:

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

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

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

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

Я обнаружил, что лучший подход для поиска символа в очень большой строке (например, длиной 1 000 000 символов) - это использовать replace() метод.

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

Вы можете увидеть еще один набор JSPerf для тестирования этого метода наряду с другими методами поиска символа в строке.

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

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

Использование со срезом

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

      function countChar (char: string, search: string): number {
  
  let num: number = 0;
  let str: string = search;
  let pos: number = str.indexOf(char);
  
  while(pos > -1) {
    str = str.slice(pos + 1);
    pos = str.indexOf(char);
    num++;
  }

  return num;

}

// Call the function
countChar('x', 'foo x bar x baz x') // 3

Использование с IndexOf из позиции

Аналогично первому подходу с использованием но вместо увеличения строки, которую мы ищем, он будет использовать параметр в метод.

      function countChar (char: string, str: string): number {
  
  let num: number = 0;
  let pos: number = str.indexOf(char);
  
  while(pos > -1) {
    pos = str.indexOf(char, pos + 1);
    num++;
  }

  return num;

}

// Call the function
countChar('x', 'foo x bar x baz x') // 3

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

Производительность 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");

Вот мое решение. Много решений уже выложено до меня. Но я люблю делиться своим мнением здесь.

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

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

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 Stackru.com' для строчной буквы 'o', вы бы использовали:

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

amount_of_os будет равно 2,


Если бы мы снова искали ту же строку, используя сопоставление без учета регистра, вы бы использовали:

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

Этот раз, amount_of_os будет равно 3, так как столица O из строки попадает в поиск.

s = 'dir/dir/dir/dir/'
for(i=l=0;i<s.length;i++)
if(s[i] == '/')
l++

Я только что сделал очень быстрый и грязный тест на https://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

Самый простой способ, который я узнал...

Пример-

str = 'mississippi';

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

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

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

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

      function len(text,char){

return text.innerText.split(string).length
}

console.log(len("str1,str2,str3,str4",","))

Это очень короткая функция.

Как насчет string.split(requiredCharecter).length-1

Пример:

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

Аналогичен прототипу, основанному выше, но не выделяет массив для строки. Распределение — это проблема почти всех вышеперечисленных версий, за исключением вариантов циклов. Это позволяет избежать кода цикла, повторно используя реализованную в браузере функцию Array.reduce.

Самый быстрый метод, кажется, через оператор индекса:

function charOccurances (str, char)
{
    for (var c = 0, i = 0, len = str.length; i < len; ++i)
    {
        if (str[i] == char)
        {
            ++c;
        }
    }
    return c;
}

Использование:

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

Использование:

charOccurances('example/path/script.js', '/') == 2

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

Второе:

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

И есть:

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

Работает с целыми числами тоже!

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

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

Далее используется регулярное выражение для проверки длины. 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.");
}

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

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

let str = "aabgrhaab"
let charMap = {}

for(let char of text) {
   if(charMap.hasOwnProperty(char)){
      charMap[char]++
   } else {
     charMap[char] = 1
   }
}

console.log(charMap); //{a: 4, b: 2, g: 1, r: 1, h: 1}

Другие вопросы по тегам