如何计算 Ruby 字符串中唯一的多个单词?

How do I count unique multiple words in a Ruby string?

正在尝试编写一个 Ruby 代码来计算唯一单词和 return 它们的总出现次数。

所以假设我想在下面的句子中查找 Sally、Marina 和 Tina 的出现次数 "Monday Tina will meet Sally and Harris. Then Tina will visit her mom Marina. Marina and Tina will meet David for dinner."

我尝试了以下方法,但这打败了干校长。有没有更好的办法?

string = "Monday Tina will meet Sally and Harris. Then Tina will visit her mom Marina. Marina and Tina will meet David for dinner. Sally will then take Tina out for a late night party." 

puts "Marina appears #{string.split.count("brown").to_i} times."
puts "Tina appears #{string.split.count("grey").to_i} times."
puts "Sally appears #{string.split.count("blue").to_i} times."

预期结果:程序在文本中查找独特的单词并 returns 它们。

实际:我必须在其自己的 PUTS 行上对每个唯一单词进行硬编码,然后执行 string.split.count(对于那个唯一单词)

注意: 我尝试了以下,但这给了我每一个字。我需要对其进行改进,以便只提供我需要的内容。这就是我挣扎的地方。

def cw(string)
  w = string.split(' ')
  freq = Hash.new(0)
  w.each { |w| freq[w.downcase] += 1 }
  return freq
end
puts cw(string)

只获取所需的人名:

people = ['Marina', 'Tina', 'Sally', 'Dory']
tmp = string.scan(/\w+/).keep_if{ |w| people.include? w }
counts people.map{ |name| [name, tmp.count{|n| n == name }] }.to_h
counts #=> {"Marina"=>2, "Tina"=>4, "Sally"=>2, "Dory"=>0}

这会将 peopole 数组针对 tmp 映射到包含 [name, count] 的嵌套数组,然后转换为散列。

好的是returns0人不出现,见'Dory'


要获得总数,有两种方法:

tmp.size #=> 8
counts.values.sum #=> 8
def count_em(str, who)
  str.gsub(/\b(?:#{who.join('|')})\b/i).
      each_with_object(Hash.new(0)) { |person,h| h[person] += 1 }
end

str = "Monday Tina will meet Sally and Harris. Then Tina will visit her " +
      "mom Marina. Marina and Tina will meet David for dinner. Sally will " +
      "then take Tina out for a late night party." 

who = %w| Sally Marina Tina |

count_em(str, who)
  #> {"Tina"=>4, "Sally"=>2, "Marina"=>2}

第一步如下。

r = /\b(?:#{who.join('|')})\b/i
  #=> /\b(?:Sally|Marina|Tina)\b/i
enum = str.gsub(r)
  #=> #<Enumerator: "Monday Tina will meet Sally and Harris. Then
  #   ...
  #   for a late night party.":gsub(/\b(?:Sally|Marina|Tina)\b/i)>

我们可以将其转换为数组以查看将传递给 each_with_object 的值。

enum.to_a
  #=> ["Tina", "Sally", "Tina", "Marina", "Marina", "Tina", "Sally", "Tina"]

然后我们简单地计算由 enum 生成的唯一值的实例数。

enum.each_with_object(Hash.new(0)) { |person,h| h[person] += 1 }
  #=> {"Tina"=>4, "Sally"=>2, "Marina"=>2}

参见 String#gsub,特别是只有一个参数且没有块的情况。诚然,这是 gsub 的不寻常用法,因为它没有进行任何替换,但在这里我更喜欢它而不是 String#scan,因为 gsub returns 是一个枚举器,而 scan 生成一个临时数组.

另请参阅 Hash::new,其中 new 接受参数且没有块的情况。该参数称为 默认值 。如果 h 是这样定义的散列,如果 h 没有键 k,则 h[k] 返回默认值。哈希没有改变。

此处默认值为零。当表达式 h[person] += 1 被解析时,它被转换为:

h[person] = h[person] + 1

如果person等于"Tina",并且是枚举器第一次生成"Tina"传递给block,h不会有key "Tina",所以表达式变成:

h["Tina"] = 0 + 1

因为 0 是默认值。下一次将 "Tina" 传递给区块时,哈希有一个键 "Tina"(值为 1),因此执行以下计算。

h["Tina"] = h["Tina"] + 1 #=> 1 + 1 #=> 2