检查一个字符在字符串中出现的次数

6

我想知道一个字符在字符串中出现的次数,但是我只能用简单的for循环解决问题。除了使用正则表达式之外,是否有更快捷、更优雅的方法可以解决这个问题?

function countCharacter(str, char) {

  var count = 0;
  for(var i = 0; i < str.length; i++){
    if(str.charAt(i) === char)
      count++;
  }
 return count;
}

4
您的函数整体可以被替换为 return str.split(char).length - 1;,额外奖励,您还可以在字符串中搜索出现的字符串 :p - Jaromanda X
1
更优美地说,是的,抽象化总有潜力。更快 - 不,你不能避免查看字符串的每个字符。 - Bergi
您可以将 str.charAt(i) 更改为 str[i],以保持本质上相同的算法并减少函数调用。 除了使用正则表达式之外 - 为什么不使用正则表达式? - nnnnnn
你在这里不能比O(n)更快,因为你必须确切地转动每一块石头。我喜欢保持简单,就像你的代码一样,我认为其他花哨的方法只是为了炫耀:D - TonyGW
你无法比O(n)更快,但常数非常重要,特别是对于JS来说,原生代码可以比解释执行快得多。如果速度是您的主要关注点,请参阅我的答案以获取性能测试。 - Flight Odyssey
重复的问题已经在这里解决了:https://dev59.com/fHE85IYBdhLWcg3wMAbc - Julian
6个回答

5
市场上有很多种可能的方法。我会添加其中的一些。
方法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

方法二:
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

编辑:根据评论,我们还可以使用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

为什么你会使用.map()而不是单独使用.filter()呢? - nnnnnn

1

有几种方法可以实现,你可以使用split/for/regex/reduce/indexOf等方式:

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

通过在字符串中运行100万次计数'/'来测试它们的性能。

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

结论('>'表示'性能更好'):

for|split|indexOf > regex > reduce

此外,如果字符串包含更多的搜索字符(如case2),

for>split>indexOf

否则(如case1)

indexOf > split > for

顺便说一句:您可以将for indexOf方法更改为适合多字符搜索(示例是单个字符)


在Chrome 89上,for循环略微优于indexOf。jsbench链接https://jsbench.me/1gkm1ma8s0/1 - Colin D

0
你有没有想过使用split()方法?这个怎么样 -
function myFunction(str, char) {    
    return string.split(char).length - 1
}

请告诉我您对这个解决方案的看法。


这是最受赞同答案的一部分复制。:S - Itération 122442
抱歉,我没有意识到。我被最受欢迎的答案的写法搞混了。发现这个更简单。想着或许一些简单性能够帮助某人更好地理解它。 - Viraj

0

我猜这涉及到正则表达式,而你想要避免它,但这很快:

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

你也可以尝试Jaromanda建议的str.split(char).length-1方法。

或者,可以使用一些有趣的递归(将0传递给startingFrom):

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

你可以牺牲一些效率来摆脱烦人的额外参数:

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

这里有一个经过速度优化的版本(在我的浏览器上比您的原始版本快约3倍,并且根据jsperf测试比正则表达式版本更快):

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

请注意,indexOf 方法通常比手动迭代字符串要快得多。请参见 jsperf

如果您使用RegExp,可以返回str.match(new RegExp(char, 'g')).length。但是请注意,使用RegExp时需要小心char的内容。 - Jaromanda X
没错,这甚至可能更加优雅。 - Flight Odyssey
然后是 str.replace(new RegExp('[^' + char + ']','g'), '').length。;-) 请注意,match 永远不会返回 0,而是会返回 null,并且 null.length 会抛出错误。:-( 所以也许可以使用 (str.match(new RegExp(char, 'g')) || []).length - RobG

0

使用reduce函数:

function countCharacter(str, char) {
    return str.split('').reduce((a, x) => x === char ? ++a : a, 0);
}

0

给你,一行代码

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

'l'替换为任何字符,new RegExp('l','g')

就是这样

str.match(new RegExp(char,'g')).length

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接