2015-12-18 3 views
0

Я работаю над программой ruby, которая возьмет строку и сравните ее с «словарем» слов и вернет хэш, с какими словами совпадают и сколько раз они совпадают. Пока я могу выполнять итерацию по строке и массиву, и она вернет строку, когда найдет совпадение, но я не знаю, как создать хеш с соответствующим словом и появлением. Вот код-Создать хеш совпадающих слов, появление

dictionary = ["below","down","go","going","horn","how","howdy","it","i","low","own","part","partner","sit"] 

def substrings (string, dictionary) 
    dictionary = dictionary 
    words = string.split(/\s+/) 
    puts words 
    x = 0 
    while x < words.length do 
    y = 0 
    while y < dictionary.length do 
     if words[x] == dictionary[y] 
     puts "it's working" 
    end 
    y += 1 
    end 
    x += 1 
    end 
end 

substrings("let's go down below", dictionary) 

Любые идеи о том, как сделать хеш, были бы очень благодарны, спасибо!

+2

Приведя пример, лучше всего показать желаемый или ожидаемый результат. Я предлагаю вам изменить это, возможно, сразу после определения словаря. Хорошо, что вы включили локальную переменную 'dictionary', чтобы читатели могли ссылаться на нее, не определяя ее. –

+2

Взгляните на документацию Ruby Hash - есть примеры того, как ее создать и получить к ней - http://docs.ruby-lang.org/en/2.0.0/Hash.html –

+1

На самом деле это два вопроса: Как подсчитать частоту слов в строке? »И« Как извлечь определенные значения из хэша? » Исследование тех, и у вас будет ваш ответ. Подсказка: это очень распространенные вопросы о переполнении стека, поэтому у вас не должно возникнуть проблем с поиском ответов. –

ответ

3

Размышлять об этом:

'b c c d'.split # => ["b", "c", "c", "d"] 
'b c c d'.split.group_by{ |w| w } # => {"b"=>["b"], "c"=>["c", "c"], "d"=>["d"]} 
'b c c d'.split.group_by{ |w| w }.map{ |k, v| [k, v.count] } # => [["b", 1], ["c", 2], ["d", 1]] 
'b c c d'.split.group_by{ |w| w }.map{ |k, v| [k, v.count] }.to_h # => {"b"=>1, "c"=>2, "d"=>1} 

Из этого мы можем построить:

dictionary = ['b', 'c'] 
word_count = 'b c c d'.split.group_by{ |w| w }.map{ |k, v| [k, v.count] }.to_h 
word_count.values_at(*dictionary) # => [1, 2] 

Если вы хотите пары ключей/значений, которые находятся в словаре, вы можете сделать это легко:

require 'active_support/core_ext/hash/slice' 
word_count.slice(*dictionary) # => {"b"=>1, "c"=>2} 

group_by - очень полезный метод, который группируется по любым критериям, которые вы передаете ему. values_at принимает список «ключей» и возвращает их соответствующие значения.

Существуют потенциальные проблемы при подсчете «слов», поскольку не весь текст приводит к тому, что мы рассмотрим слово после его разбиения на его составные подстроки. Например:

'how now brown cow.'.split # => ["how", "now", "brown", "cow."] 

Обратите внимание, что последнее слово имеет пунктуацию, включенную в строку. Аналогичным образом, составные слова и другая пунктировка могут вызвать проблемы:

'how-now brown, cow.'.split # => ["how-now", "brown,", "cow."] 

Задачи становится как удалить из тех, которые рассматриваются в качестве частей слов. Простая вещь, чтобы просто лишить их:

'how-now brown, cow.'.gsub(/[^a-z]+/, ' ').split # => ["how", "now", "brown", "cow"] 

В современном сумасшедшем возрасте, хотя, мы видим слова, которые содержат цифры тоже, особенно такие вещи, как названия компаний и программ. Вы можете изменить шаблон в gsub выше, чтобы обработать это, но как вам осталось выяснить.

Мы также видим смешанный футляр, поэтому ваш словарь нужно сгибать в верхний или нижний регистр, а рассматриваемая строка также должна быть свернута одинаково, , если только вы хотите знать разные значения когда почитание характер дела:

word_count = 'b C c d'.downcase.split.group_by{ |w| w }.map{ |k, v| [k, v.count] }.to_h # => {"b"=>1, "c"=>2, "d"=>1} 
word_count = 'b C c d'.split.group_by{ |w| w }.map{ |k, v| [k, v.count] }.to_h # => {"b"=>1, "C"=>1, "c"=>1, "d"=>1} 

анализируя содержание страниц часто начинается с такого рода кода, но многие правила должны быть написаны, чтобы указать, какие полезные слова и то, что является мусором. И, правила часто меняются от одного источника к другому, как их использование слов и цифр может нарушить полезность вашего кода быстро:

second 
2nd 

, например. Это становится «интересным».

2

Один из способов сделать это, чтобы создать то, что иногда называют «подсчета хэш»:

h = Hash.new(0) 

Здесь ноль является «значение по умолчанию». Это означает, что если h не имеет ключа k, h[k]возвращает ноль (но хэш не изменяется). Тогда вы будете иметь:

h[k] += 1 

который расширяется:

h[k] = h[k] + 1 

Если h имеет ключ k, h[k] справа будет иметь значение, так Bob's your uncle. Если, однако, h не имеет ключа k, h[k] справа, устанавливается значение по умолчанию, так что выражение становится:

h[k] = 0 + 1 

Круто, а?

Так что для вашей проблемы, вы можете написать:

dictionary = %w| below down go going horn how howdy it i low own part partner sit | 
    #=> ["below", "down", "go", "going", "horn", "how", "howdy", "it", "i", 
    # "low", "own", "part", "partner", "sit"] 
string = "Periscope down, so we can go down, way down, below the surface." 

string.delete(',.').split.downcase.each_with_object(Hash.new(0)) { |word,h| 
    (h[word] += 1) if dictionary.include?(word) } 
    #=> {"down"=>3, "go"=>1, "below"=>1} 

Вы также можете увидеть это написано:

string.delete(',.').downcase.split.each_with_object({}) do |word,h| 
    h[word.downcase] = (h[word] || 0) + 1 if dictionary.include?(word) } 

так что если h не имеет ключ word, h[word] будет nil, поэтому выражение будет:

h[word] = (h[word] || 0) + 1 
    #=> = (nil  || 0) + 1 
    #=> = 0 + 1 

Другой подход заключается в первую подсчитывают количество экземпляров каждого слова в string затем посмотреть, которые находятся в словаре:

h = string.delete(',.').downcase.split.group_by(&:itself) 
    #=> {"periscope"=>["periscope"], "down"=>["down", "down", "down"], "so"=>["so"], 
    # "we"=>["we"], "can"=>["can"], "go"=>["go"], "way"=>["way"], "below"=>["below"], 
    # "the"=>["the", "the"], "surface"=>["surface"]} 
h.each_with_object({}) { |(k,v),g| g[k] = v.size if dictionary.include?(k) } 
    #=> {"down"=>3, "go"=>1, "below"=>1} 

(Edit: см @ ответ theTinMan для более эффективных способов использования Enumerable#group_by).

3

Вот еще один способ сделать это:

def substrings (string, dictionary) 
    dictionary.each.with_object({}){|w, h| h[w] = string.scan(/\b#{w}\b/).length} 
end 

substrings("let's go down below", dictionary) 

выход:

{ 
    "below" => 1, 
    "down" => 1, 
    "go"  => 1, 
    "going" => 0, 
    "horn" => 0, 
    "how"  => 0, 
    "howdy" => 0, 
    "it"  => 0, 
    "i"  => 0, 
    "low"  => 0, 
    "own"  => 0, 
    "part" => 0, 
    "partner" => 0, 
    "sit"  => 0 
} 
+1

Я всегда предпочитаю решения проблем с строкой, которые, как и ваши, обрабатывают строку напрямую, а не сначала разбивают ее на массив подстрок. –

2

здание на вершине описания подсчета Hash дается Cary ваш код может быть изменен немного, как показано ниже.

dictionary = ["below","down","go","going","horn","how","howdy","it","i","low","own","part","partner","sit"] 

def substrings (string, dictionary) 

    words = string.split(/\s+/) 

    count_hash = Hash.new(0) 

    words.each do |sentence_word| 
    dictionary.each do |dictionary_word| 
     if sentence_word == dictionary_word 
      count_hash[sentence_word] += 1 
     end 
    end 
    end 

    return count_hash 
end 

p substrings("let's go down below", dictionary) 

Однако, учитывая, что существует метод Array#count, мы могли бы взять его преимущество и сократить код выше, чтобы что-то, как показано ниже. В этой версии нам не нужно считать хэш.

def substrings (string, dictionary) 
    words = string.split(/\s+/) 
    count_hash = Hash.new 

    dictionary.each do |dictionary_word| 
    if (count = words.count(dictionary_word)) > 0 
     count_hash[dictionary_word] = count 
    end 
    end 

    return count_hash 
end 

Вы можете обратиться к другим ответам для получения более идиоматических решений Ruby. Если бы я должен был принять удар на него, ниже будет моя версия

def substrings (string, dictionary) 
    words = string.split(/\s+/) 
    dictionary.map { |d| [d, words.count(d)] }.to_h.reject {|_, v| v == 0} 
end 
+0

Ты не создатель жезлов. Жезл имеет заостренную голову. –

+0

@CarySwoveland Ни одна из зимних шляпок bash выглядела аккуратно, когда у меня была моя заостренная шляпа. Так что временно его шляпы-off –

+1

Вариант: 'words = string.split; (словарь и слова) .map {| w | [w, words.count (w)]} .to_h'. (Примечание 'str.split' совпадает с' str.split (/ \ s + /) '.) –

Смежные вопросы