【问题标题】:Most common words in string字符串中最常见的词
【发布时间】:2014-12-10 21:40:14
【问题描述】:

我是 Ruby 新手,正在尝试编写一个方法,该方法将返回字符串中最常见单词的数组。如果有一个单词计数很高,则应返回该单词。如果有两个单词与高计数相关,则两者都应在数组中返回。

问题是当我通过第二个字符串时,代码只计算“单词”两次而不是三次。当第三个字符串通过时,它返回计数为 2 的 "it",这没有意义,因为 "it" 的计数应该为 1。

def most_common(string)
  counts = {}
  words = string.downcase.tr(",.?!",'').split(' ')

  words.uniq.each do |word|
    counts[word] = 0
  end

  words.each do |word|
    counts[word] = string.scan(word).count
  end

  max_quantity = counts.values.max
  max_words = counts.select { |k, v| v == max_quantity }.keys
  puts max_words
end

most_common('a short list of words with some words') #['words']
most_common('Words in a short, short words, lists of words!') #['words']
most_common('a short list of words with some short words in it') #['words', 'short']

【问题讨论】:

  • 感谢大家的帮助。经过仔细检查,我发现在 words.each 中,我正在查看“字符串”而没有进行小写,这似乎解决了我的两个问题。
  • @NickVeys 给出了一个很好的答案(赢得了我的 +1),并且是唯一一个回答你的问题的人,所以你会用绿色复选标记来奖励它是可以理解的。但是,我建议您在将来选择答案之前推迟一段时间(可能是一个小时或更长时间),因为相对快速的选择往往会阻止其他可能更好的答案,并且还会抢占仍在准备答案的读者。
  • 会的。对这一切仍然很陌生,并且正在学习其中的技巧。
  • This faq 值得一读。

标签: ruby arrays string hash


【解决方案1】:

您计算单词实例的方法是您的问题。 itwith 中,所以是重复计算的。

[1] pry(main)> 'with some words in it'.scan('it')
=> ["it", "it"]

这可以更简单,您可以使用 each_with_object 调用按值的实例数对数组的内容进行分组,如下所示:

counts = words.each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }

这会遍历数组中的每个条目,并将哈希中每个单词条目的值加 1。

所以以下应该适合你:

def most_common(string)
  words = string.downcase.tr(",.?!",'').split(' ')
  counts = words.each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }
  max_quantity = counts.values.max
  counts.select { |k, v| v == max_quantity }.keys
end

p most_common('a short list of words with some words') #['words']
p most_common('Words in a short, short words, lists of words!') #['words']
p most_common('a short list of words with some short words in it') #['words', 'short']

【讨论】:

  • 很好的答案。我可以建议对您的 most_common 方法的前两行稍作改进吗? words = string.scan(/\w+/); counts = words.each_with_object(Hash.new(0)) {|word, counts| counts[word.downcase] += 1 }
  • 当然!我只是想保留一些原作。总是有改进的余地。
【解决方案2】:

尼克已经回答了你的问题,我只是建议另一种可以做到这一点的方法。由于“高计数”含糊不清,我建议您返回一个带有小写单词及其各自计数的哈希。从 Ruby 1.9 开始,哈希保留键值对的输入顺序,因此我们可能希望利用它并返回键值对按值降序排列的哈希。

代码

def words_by_count(str)
  str.gsub(/./) do |c|
    case c
    when /\w/ then c.downcase
    when /\s/ then c
    else ''
    end
  end.split
     .group_by {|w| w}
     .map {|k,v| [k,v.size]}
     .sort_by(&:last)
     .reverse
     .to_h
end
words_by_count('Words in a short, short words, lists of words!')

Array#h 方法是在 Ruby 2.1 中引入的。对于早期的 Ruby 版本,必须使用:

Hash[str.gsub(/./)... .reverse]

示例

words_by_count('a short list of words with some words')
  #=> {"words"=>2, "of"=>1, "some"=>1, "with"=>1,
  #    "list"=>1, "short"=>1, "a"=>1}
words_by_count('Words in a short, short words, lists of words!')
  #=> {"words"=>3, "short"=>2, "lists"=>1, "a"=>1, "in"=>1, "of"=>1}
words_by_count('a short list of words with some short words in it')
  #=> {"words"=>2, "short"=>2, "it"=>1, "with"=>1,
  #    "some"=>1, "of"=>1, "list"=>1, "in"=>1, "a"=>1}

说明

这是第二个示例中发生的情况,其中:

str = 'Words in a short, short words, lists of words!'

str.gsub(/./) do |c|... 匹配字符串中的每个字符并将其发送到块以决定如何处理它。如您所见,单词字符被小写,空格被单独保留,其他所有内容都转换为空格。

s = str.gsub(/./) do |c|
      case c
      when /\w/ then c.downcase
      when /\s/ then c
      else ''
      end
    end
  #=> "words in a short short words lists of words"

接下来是

a = s.split
 #=> ["words", "in", "a", "short", "short", "words", "lists", "of", "words"]
h = a.group_by {|w| w}
 #=> {"words"=>["words", "words", "words"], "in"=>["in"], "a"=>["a"],
 #    "short"=>["short", "short"], "lists"=>["lists"], "of"=>["of"]}
b = h.map {|k,v| [k,v.size]}
 #=> [["words", 3], ["in", 1], ["a", 1], ["short", 2], ["lists", 1], ["of", 1]]
c = b.sort_by(&:last)
 #=> [["of", 1], ["in", 1], ["a", 1], ["lists", 1], ["short", 2], ["words", 3]]
d = c.reverse
 #=> [["words", 3], ["short", 2], ["lists", 1], ["a", 1], ["in", 1], ["of", 1]]
d.to_h # or Hash[d]
 #=> {"words"=>3, "short"=>2, "lists"=>1, "a"=>1, "in"=>1, "of"=>1}

注意c = b.sort_by(&:last)d = c.reverse可以替换为:

d = b.sort_by { |_,k| -k }
 #=> [["words", 3], ["short", 2], ["a", 1], ["in", 1], ["lists", 1], ["of", 1]]

sort 后跟reverse 通常更快。

【讨论】:

    【解决方案3】:
    def count_words string
      word_list = Hash.new(0)
      words     = string.downcase.delete(',.?!').split
      words.map { |word| word_list[word] += 1 }
      word_list
    end
    
    def most_common_words string
      hash      = count_words string
      max_value = hash.values.max
      hash.select { |k, v| v == max_value }.keys
    end
    
    most_common 'a short list of words with some words'
    #=> ["words"]
    
    most_common 'Words in a short, short words, lists of words!'
    #=> ["words"]
    
    most_common 'a short list of words with some short words in it'
    #=> ["short", "words"]
    

    【讨论】:

      【解决方案4】:

      假设 string 是一个包含多个单词的字符串。

      words = string.split(/[.!?,\s]/)
      words.sort_by{|x|words.count(x)}
      

      这里我们将单词拆分为一个字符串并将它们添加到一个数组中。然后我们根据单词的数量对数组进行排序。最常用的单词会出现在最后。

      【讨论】:

        【解决方案5】:

        同样的事情也可以通过以下方式完成:

        def most_common(string)
          counts = Hash.new 0
          string.downcase.tr(",.?!",'').split(' ').each{|word| counts[word] += 1}
          # For "Words in a short, short words, lists of words!"
          # counts ---> {"words"=>3, "in"=>1, "a"=>1, "short"=>2, "lists"=>1, "of"=>1} 
          max_value = counts.values.max
          #max_value ---> 3
          return counts.select{|key , value| value == counts.values.max}
          #returns --->  {"words"=>3}
        end
        

        这只是一个较短的解决方案,您可能想要使用它。希望对你有帮助:)

        【讨论】:

          【解决方案6】:

          这是程序员喜欢的问题,不是吗:) 函数式方法怎么样?

          # returns array of words after removing certain English punctuations
          def english_words(str)
            str.downcase.delete(',.?!').split
          end
          
          # returns hash mapping element to count
          def element_counts(ary)
            ary.group_by { |e| e }.inject({}) { |a, e| a.merge(e[0] => e[1].size) }
          end
          
          def most_common(ary)
            ary.empty? ? nil : 
              element_counts(ary)
                .group_by { |k, v| v }
                .sort
                .last[1]
                .map(&:first)
          end
          
          most_common(english_words('a short list of words with some short words in it'))
          #=> ["short", "words"]
          

          【讨论】:

            【解决方案7】:
            def firstRepeatedWord(string)
              h_data = Hash.new(0)
              string.split(" ").each{|x| h_data[x] +=1}
              h_data.key(h_data.values.max)
            end
            

            【讨论】:

              【解决方案8】:
              def common(string)
                counts=Hash.new(0)
                words=string.downcase.delete('.,!?').split(" ")
                words.each {|k| counts[k]+=1}
                p counts.sort.reverse[0]
              end
              

              【讨论】:

                猜你喜欢
                • 2014-12-14
                • 1970-01-01
                • 2023-04-04
                • 2018-04-01
                • 2012-12-08
                • 1970-01-01
                • 2015-11-29
                • 2011-02-03
                • 2014-05-24
                相关资源
                最近更新 更多