5

Simply trying to find how many times a given character appears in a string but I can't solve it any other way then this simple for-loop. Is there a method that would solve this quicker or more eloquently other than using Regex?

function countCharacter(str, char) {

  var count = 0;
  for(var i = 0; i < str.length; i++){
    if(str.charAt(i) === char)
      count++;
  }
 return count;
}
ayeteo
  • 234
  • 2
  • 5
  • 14
  • 4
    the whole body of your function can be replaced with `return str.split(char).length - 1;` - bonus, you can search for occurrence of strings in string :p – Jaromanda X Aug 10 '17 at 02:13
  • 1
    More eloquently - yes, there's always potential for abstraction. Quicker - no, you can't get around looking at every character of the string. – Bergi Aug 10 '17 at 02:24
  • You can change `str.charAt(i)` to `str[i]` to keep essentially the same algorithm with fewer function calls. *"other than using Regex"* - Why not regex? – nnnnnn Aug 10 '17 at 02:45
  • you can't be faster than O(n) here, because you have to turn every stone exactly once. I like to keep it simple like your code, I consider other fancy ways to be a show-off :D – TonyGW Aug 10 '17 at 02:45
  • You can't get faster than O(n), but constants matter a lot, especially for JS where native code can be much faster than interpreted. See my answer for performance tests if speed is your main concern – Flight Odyssey Aug 10 '17 at 02:48
  • Duplicated question and already solved here: https://stackoverflow.com/questions/2903542/javascript-how-many-times-a-character-occurs-in-a-string – Julian Apr 29 '22 at 08:30

5 Answers5

4

There are many possible ways are available in the market. I am adding a few of them.

Method 1:

str = "The man is as good as his word"
str.split('a')
output: (4) ["The m", "n is ", "s good ", "s his word"]
str.split('a').length - 1
output: 3

Method 2:

str = "The man is as good as his word"
str.split('').map( function(char,i)
    { if(char === 'a') 
        return i;
    } 
).filter(Boolean)
Output: (3) [5, 11, 19]

str.split('').map( function(char,i)
    { if(char === 'a') 
        return i;
    } 
).filter(Boolean).length

ouput: 3

Edit: As per comment we can also make use of filter().

    str.split('').filter(function(char, i){
        if(char == 'a'){
            return i;
        }
    })
    output: (3) ["a", "a", "a"]

str.split('').filter(function(char, i){
    if(char == 'a'){
        return i;
    }
}).length
output: 3
Neha Chopra
  • 1,701
  • 10
  • 11
1

----edited by adding more cases from answers----

there are several ways, you can use split/for/regex/reduce/indexOf like this:

function countCharacter_reduce(str, ch) {
  return Array.prototype.reduce.call(str, (prev, cur) => cur === ch && ++prev && prev, 0);
}

function countCharacter_split(str, ch) {
  return str.split(ch).length - 1;
}

function countCharacter_for(str, ch) {
  for (var count = 0, ii = 0; ii < str.length; ii++) {
    if (str[ii] === ch)
      count++;
  }
  return count;
}

function countCharacter_regex(str, ch) {
  return str.length - str.replace(new RegExp(ch, 'g'), '').length;
}

function countCharacter_indexOf(str, char) {
  var start = 0;
  var count = 0;
  while ((start = str.indexOf(char, start) + 1) !== 0) {
    count++;
  }
  return count;
}

performance of them by running 1,000,000 times on counting '/' in a string.

-- case1: running 1000000 times on ( 'this/is/a/path/with/extension', '/' )
countCharacter_reduce: 2389.880ms
countCharacter_regex: 496.251ms
countCharacter_split: 114.709ms
countCharacter_for: 152.012ms
countCharacter_indexOf: 90.330ms

-- case2: running 1000000 times on ( '////////////', '/' )
countCharacter_reduce: 1138.051ms
countCharacter_regex: 619.886ms
countCharacter_split: 121.945ms
countCharacter_for: 74.542ms
countCharacter_indexOf: 204.242ms

Conclusion ('>' means 'has better performance'):

for|split|indexOf > regex > reduce.

furthermore, if the string contains more searching characters (like case2),

for>split>indexOf,

otherwise (like case1)

indexOf > split > for.

BTW: you can change the for indexOf method to fit multi-characters search (example is single character)

aaron
  • 1,795
  • 3
  • 25
  • 38
  • On Chrome 89, the for loop slightly outperforms the indexOf. jsbench link https://jsbench.me/1gkm1ma8s0/1 – Colin D Mar 09 '21 at 06:13
0

I guess this involves regex which you wanted to avoid but it's pretty quick:

function countCharacter(str, char) {
    return str.length - str.replace(new RegExp(char,"g"),"").length;
}

You can also try the str.split(char).length-1 approach suggested by Jaromanda.

Or, go all out with some fun recursion (pass 0 to startingFrom):

function countCharacter(str, char, startingFrom) {
    var idx = str.indexOf(char, startingFrom);
    return idx == -1 ? 0 : 1 + countCharacter(str, char, idx + 1);
}

You can get rid of the annoying extra argument at the cost of some efficiency:

function countCharacter(str, char) {
    var idx = str.indexOf(char);
    return idx == -1 ? 0 : 1 + countCharacter(str.substr(idx+1), char);
}

And here's a version optimized for speed (this is about 3 times faster on my browser than your original and much faster than the regex versions, according to jsperf):

function countCharacter(str, char) {
   var start = 0;
   var count = 0;
   while((start = str.indexOf(char, start)+1) !== 0) {
       count++;
   }
   return count;
}

Note that the indexOf approaches will generally be substantially faster than manually iterating through the string. See jsperf

Flight Odyssey
  • 2,257
  • 17
  • 25
  • or simply `str.match(new RegExp(char, 'g')).length` if you use RegExp - with RegExp, you need to be careful of what `char` is though – Jaromanda X Aug 10 '17 at 02:27
  • True, that's even a bit more elegant probably – Flight Odyssey Aug 10 '17 at 02:28
  • Then there is `str.replace(new RegExp('[^' + char + ']','g'), '').length`. ;-) Note that *match* can never return 0, it will return *null* instead and `null.length` will throw an error. :-( So maybe `(str.match(new RegExp(char, 'g')) || []).length`. – RobG Aug 10 '17 at 02:43
0

using reduce:

function countCharacter(str, char) {
    return str.split('').reduce((a, x) => x === char ? ++a : a, 0);
}
guijob
  • 4,145
  • 1
  • 19
  • 35
0

Here you go. One line code

"hello".match(new RegExp('l','g')).length

replace 'l' with any char here, new RegExp('l','g').

that is

str.match(new RegExp(char,'g')).length
not_python
  • 844
  • 6
  • 13