Ruby: How to count the number of times a string appears in another string?

RubyStringCount

Ruby Problem Overview


I'm trying to count the number of times a string appears in another string.

I know you can count the number of times a letter appears in a string:

string = "aabbccddbb"
string.count('a')
=> 2

But if I search for how many times 'aa' appears in this string, I also get two.

string.count('aa')
=> 2

I don't understand this. I put the value in quotation marks, so I'm searching for the number of times the exact string appears, not just the letters.

Ruby Solutions


Solution 1 - Ruby

Here are a couple of ways to count the numbers of times a given substring appears in a string (the first being my preference). Note (as confirmed by the OP) the substring 'aa' appears twice in the string 'aaa', and therefore five times in:

str = "aaabbccaaaaddbab"

#1

Use String#scan with a regex that contains a positive lookahead that looks for the substring:

def count_em(str, substr)
  str.scan(/(?=#{substr})/).count
end


count_em(str,"aa") #=> 5 count_em(str,"ab") #=> 2

Note:

"aaabbccaaaaddbab".scan(/(?=aa)/)
  #=> ["", "", "", "", ""]

A positive lookbehind produces the same result:

"aaabbccaaaaddbab".scan(/(?<=aa)/)
  #=> ["", "", "", "", ""]

As well, String#scan could be replaced with the form of String#gsub that takes one argument (here the same regular expression) and no block, and returns an enumerator. That form of gsub in unusual in that has nothing to do with character replacement; it simply generates matches of the regular expression.

#2

Convert to an array, apply String#each_char then Enumerable#each_cons, then Enumerable#count:

def count_em(str, substr)
  subarr = substr.chars
  str.each_char
     .each_cons(substr.size)
     .count(subarr)
end


count_em(str,"aa") #=> 5 count_em(str,"ab") #=> 2

We have:

subarr = "aa".chars
  #=> ["a", "a"]
enum0 = "aaabbccaaaaddbab".each_char
  #=> #<Enumerator: "aaabbccaaaaddbab":each_char>

We can see the elements that will generated by this enumerator by converting it to an array:

enum0.to_a
  #=> ["a", "a", "a", "b", "b", "c", "c", "a", "a", "a",
  #    "a", "d", "d", "b", "a", "b"]

enum1 = enum0.each_cons("aa".size)
  #=> #<Enumerator: #<Enumerator:
  #      "aaabbccaaaaddbab":each_char>:each_cons(2)> 

Convert enum1 to an array to see what values the enumerator will pass on to map:

enum1.to_a
  #=> [["a", "a"], ["a", "a"], ["a", "b"], ["b", "b"], ["b", "c"],
  #    ["c", "c"], ["c", "a"], ["a", "a"], ["a", "a"], ["a", "a"], 
  #    ["a", "d"], ["d", "d"], ["d", "b"], ["b", "a"],
  #    ["a", "b"]]
 
enum1.count(subarr)
  #=> enum1.count(["a", "a"])
  #=> 5

Solution 2 - Ruby

It's because the count counts characters, not instances of strings. In this case 'aa' means the same thing as 'a', it's considered a set of characters to count.

To count the number of times aa appears in the string:

string = "aabbccddbb"
string.scan(/aa/).length
# => 1
string.scan(/bb/).length
# => 2
string.scan(/ff/).length
# => 0

Solution 3 - Ruby

try to use string.split('a').count - 1

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJohnsonView Question on Stackoverflow
Solution 1 - RubyCary SwovelandView Answer on Stackoverflow
Solution 2 - RubytadmanView Answer on Stackoverflow
Solution 3 - RubyGeorgii GavrilchikView Answer on Stackoverflow