2014-10-15 2 views
4

Я новичок в Ruby и пытаюсь написать метод, который вернет массив наиболее распространенных слов в строке. Если есть одно слово с высоким значением, это слово должно быть возвращено. Если для большого подсчета есть два слова, они должны быть возвращены в массив.Наиболее распространенные слова в строке

Проблема заключается в том, что когда я проходил вторую строку, код учитывал только «слова» дважды, а не три раза. Когда третья строка передается через, она возвращает «это» с числом 2, что не имеет смысла, так как «он» должен иметь счетчик 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'] 
+0

Связанные вопрос: http://stackoverflow.com/q/10695392/1301972 –

+0

Спасибо за помощь Everone. Я обнаружил при ближайшем рассмотрении, что в словах. Я смотрел на «нитку» без сглаживания, что, казалось, решало обе мои проблемы. – ACIDSTEALTH

+0

@ NickVeys дал хороший ответ (заработал мой +1) и является единственным, кто отвечает на ваш вопрос, поэтому понятно, что вы наградите его зеленой галочкой. Тем не менее, я бы предположил, что в будущем вы можете немного отойти (возможно, час или дольше), прежде чем выбирать ответ, поскольку относительно быстрый отбор, как правило, препятствует другим, возможно, лучшим ответам, а также упреждает читателей, которые все еще готовят ответы. –

ответ

4

Ваш метод подсчета экземпляров слова это ваша проблема. it находится в with, поэтому он считается двойным.

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

Это проходит через каждую запись в array и добавляет 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'] 
+0

Отличный ответ. Могу ли я предложить небольшое уточнение для первых двух строк вашего метода 'most_common'? 'words = string.scan (/ \ w + /); counts = words.each_with_object (Hash.new (0)) {| word, counts | counts [word.downcase] + = 1} ' –

+0

Конечно! Я просто пытался сохранить часть оригинала. Всегда место для улучшения. –

0

То же самое можно сделать следующим образом тоже:

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 

Это лишь короткий решение, которое вы могли бы хотеть использовать. Надеюсь, это поможет :)

1
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"] 
0

Это вопрос, который программисты любят, не так ли :) Как насчет функционального подхода?

# 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"] 
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, как правило, быстрее.

1

Предполагая, что строка является строкой, содержащей несколько слов.

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

Здесь мы разбиваем слова в строке и добавляем их в массив. Затем мы сортируем массив на основе количества слов. Наиболее распространенные слова появятся в конце.

0
def firstRepeatedWord(string) 
    h_data = Hash.new(0) 
    string.split(" ").each{|x| h_data[x] +=1} 
    h_data.key(h_data.values.max) 
end 
Смежные вопросы