web-dev-qa-db-fra.com

Ruby: Comment compter le nombre de fois qu'une chaîne apparaît dans une autre chaîne?

J'essaie de compter le nombre de fois qu'une chaîne apparaît dans une autre chaîne.

Je sais que vous pouvez compter le nombre de fois qu'une lettre apparaît dans une chaîne:

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

Mais si je recherche le nombre de fois où 'aa' apparaît dans cette chaîne, j'en reçois également deux.

string.count('aa')
=> 2

Je ne comprends pas ça. Je mets la valeur entre guillemets, donc je recherche le nombre de fois que la chaîne exacte apparaît, pas seulement les lettres.

42
Johnson

Voici quelques façons de compter le nombre de fois qu'une sous-chaîne donnée apparaît dans une chaîne (la première étant ma préférence). Notez (comme confirmé par l'OP) la sous-chaîne 'aa' apparaît deux fois dans la chaîne 'aaa', et donc cinq fois dans:

string="aaabbccaaaaddbb"

# 1

Utilisez String # scan avec un regex qui contient un lookahead positif qui recherche la sous-chaîne:

def count_em(string, substring)
  string.scan(/(?=#{substring})/).count
end

count_em(string,"aa")
 #=> 5

Remarque:

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

Un lookbehind positif produit le même résultat:

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

Ainsi que, String#scan peut être remplacé par String # gsub .

# 2

Convertissez en tableau, appliquez Enumerable # each_cons , puis rejoignez et comptez:

def count_em(string, substring)
  string.each_char.each_cons(substring.size).map(&:join).count(substring)
end

count_em(string,"aa")
  #=> 5

On a:

enum0 = "aaabbccaaaaddbb".each_char
  #=> #<Enumerator: "aaabbccaaaaddbb":each_char>

Nous pouvons voir les éléments qui seront générés par cet énumérateur en le convertissant en un tableau:

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

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

Convertir enum1 à un tableau pour voir quelles valeurs l'énumérateur transmettra à 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", "b"]]

c = enum1.map(&:join)
  #=> ["aa", "aa", "ab", "bb", "bc", "cc", "ca",
  #    "aa", "aa", "aa", "ad", "dd", "db", "bb"]
c.count("aa")
  #=> 5
51
Cary Swoveland

C'est parce que les count comptent caractères , pas les instances de chaînes. Dans ce cas 'aa' signifie la même chose que 'a', il est considéré comme un ensemble de caractères à compter.

Pour compter le nombre de fois que aa apparaît dans la chaîne:

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