How to break while = gets - ruby

I want to run this code like this
count = Hash.new(0)
while line = gets
words = line.split
words.each do |word|
count[word] += 1
end
end
count.sort{|a, b|
a[1] <=> b[1]
}.each do |key, value|
print "#{key}: #{value}\n"
end
but I don't know how to break. And Hitting Command+C returns
word_count.rb:3:in `gets': Interrupt
from word_count.rb:3:in `gets'
from word_count.rb:3:in `<main>'
How can I fix this code?

You can also trap the signal Ctrl+C sent to the process:
count = Hash.new(0)
trap("SIGINT") {
count.sort{|a, b|
a[1] <=> b[1]
}.each do |key, value|
print "#{key}: #{value}\n"
end
exit!
}
while line = gets
words = line.split
words.each do |word|
count[word] += 1
end
end
Reference to the documentation of Ruby Signal

Try Ctrl-D. Is this what you need?

count = Hash.new(0)
stop_condition = "\n"
until stop_condition == line = gets
words = line.split
words.each do |word|
count[word] += 1
end
end
Of course, you could use a break if line.chomp.empty? inside the while loop But I avoid it, as it really is an infinite loop that you know the condition of which you want to escape from. This also doesn't exit the program, just the loop.
controlc and controld are strange for users, and I would avoid needing to use those things for non-exceptional events.

Related

How can I refactor my word frequency method?

This is my method word_frequency.
def frequencies(text)
words = text.split
the_frequencies = Hash.new(0)
words.each do |word|
the_frequencies[word] += 1
end
return the_frequencies
end
def most_common_words(file_name, stop_words_file_name, number_of_word)
# TODO: return hash of occurences of number_of_word most frequent words
opened_file_string = File.open(file_name.to_s).read.downcase.strip.split.join(" ").gsub(/[^a-zA-Z \'$]/, "").gsub(/'s/, "").split
opened_stop_file_string = File.open(stop_words_file_name.to_s).read.downcase.strip.split.join(" ").gsub(/[^a-zA-Z \']/, "").gsub(/'s/, "").split
# declarar variables de file_name stop words.
filtered_array = opened_file_string.reject { |n| opened_stop_file_string.include? n }
the_frequencies = Hash.new(0)
filtered_array.each do |word|
the_frequencies[word] += 1
end
store = the_frequencies.sort_by { |_key, value| value }.reverse[0..number_of_word - 1].to_h
store
end
Works well, but I think I can do it better. Rubocop says my lines are too long, and I'm agree, but this is my best. Can someone explain how I can do it better?
It would be good if you just decompose the big parts. The most_common_words seems still delicate, you could explain what you're trying to do, to see what can else can be done there.
You could also make use of frequencies, and looking at the pattern within the method arguments, an OOP approach would fit better here.
def join_file(file_name)
File.open(file_name).read.downcase.strip.split.join(' ')
end
def frequencies(text)
text.split.each_with_object(Hash.new(0)) { |word, hash| hash[word] += 1 }
end
def opened_file_string(file_name)
join_file(file_name).gsub(/[^a-zA-Z \'$]/, '').gsub(/'s/, '').split
end
def opened_stop_file_string(file_name)
#opened_stop_file_string ||= join_file(file_name).gsub(/[^a-zA-Z \']/, '').gsub(/'s/, '').split
end
def in_stop_file_string?(file_name, word)
opened_stop_file_string(file_name).include?(word)
end
def filtered_array(file_name, stop_words_file_name)
opened_file_string(file_name).reject do |word|
in_stop_file_string?(stop_words_file_name, word)
end
end
def frequencies_in_filtered_array(file_name, stop_words_file_name)
frequencies(filtered_array(file_name, stop_words_file_name)).sort_by { |_, value| value }
end
def most_common_words(file_name, stop_words_file_name, number_of_word)
frequencies_in_filtered_array(file_name.to_s, stop_words_file_name.to_s).reverse[0...number_of_word].to_h
end
This is a bit cleaner, use multiline method chaining etc.
def frequencies(text)
words = text.split
the_frequencies = Hash.new(0)
words.each do |word|
the_frequencies[word] += 1
end
the_frequencies
end
def pre_process_file(file_name)
File.open(file_name.to_s)
.read.downcase.strip.split.join(" ")
.gsub(/[^a-zA-Z \'$]/, "")
.gsub(/'s/, "")
.split
end
def most_common_words(file_name, stop_words_file_name, number_of_word)
# TODO: return hash of occurences of number_of_word most frequent words
opened_file_string = pre_process_file(file_name)
opened_stop_file_string = pre_process_file(stop_words_file_name)
# declarar variables de file_name stop words.
filtered_array = opened_file_string
.reject { |n| opened_stop_file_string.include? n }
the_frequencies = Hash.new(0)
filtered_array.each { |word| the_frequencies[word] += 1 }
the_frequencies
.sort_by { |_k, value| value }
.reverse[0..number_of_word - 1]
.to_h
end

Finding the most occurring character/letter in a string

Trying to get the most occurring letter in a string.
So far:
puts "give me a string"
words = gets.chomp.split
counts = Hash.new(0)
words.each do |word|
counts[word] += 1
end
Does not run further than asking for a string. What am I doing wrong?
If you're running this in irb, then the computer may think that the ruby code you're typing in is the text to analyse:
irb(main):001:0> puts "give me a string"
give me a string
=> nil
irb(main):002:0> words = gets.chomp.split
counts = Hash.new(0)
words.each do |word|
counts[word] += 1
end=> ["counts", "=", "Hash.new(0)"]
irb(main):003:0> words.each do |word|
irb(main):004:1* counts[word] += 1
irb(main):005:1> end
NameError: undefined local variable or method `counts' for main:Object
from (irb):4:in `block in irb_binding'
from (irb):3:in `each'
from (irb):3
from /Users/agrimm/.rbenv/versions/2.2.1/bin/irb:11:in `<main>'
irb(main):006:0>
If you wrap it in a block of some sort, you won't get that confusion:
begin
puts "give me a string"
words = gets.chomp.split
counts = Hash.new(0)
words.each do |word|
counts[word] += 1
end
counts
end
gives
irb(main):001:0> begin
irb(main):002:1* puts "give me a string"
irb(main):003:1> words = gets.chomp.split
irb(main):004:1> counts = Hash.new(0)
irb(main):005:1> words.each do |word|
irb(main):006:2* counts[word] += 1
irb(main):007:2> end
irb(main):008:1> counts
irb(main):009:1> end
give me a string
foo bar
=> {"foo"=>1, "bar"=>1}
Then you can work on the fact that split by itself isn't what you want. :)
This should work:
puts "give me a string"
result = gets.chomp.split(//).reduce(Hash.new(0)) { |h, v| h.store(v, h[v] + 1); h }.max_by{|k,v| v}
puts result.to_s
Output:
#Alan ➜ test rvm:(ruby-2.2#europa) ruby test.rb
give me a string
aa bbb cccc ddddd
["d", 5]
Or in irb:
:008 > 'This is some random string'.split(//).reduce(Hash.new(0)) { |h, v| h.store(v, h[v] + 1); h }.max_by{|k,v| v}
=> ["s", 4]
Rather than getting a count word by word, you can process the whole string immediately.
str = gets.chomp
hash = Hash.new(0)
str.each_char do |c|
hash[c] += 1 unless c == " " #used to filter the space
end
After getting the number of letters, you can then find the letter with highest count with
max = hash.values.max
Then match it to the key in the hash and you're done :)
puts hash.select{ |key| hash[key] == max }
Or to simplify the above methods
hash.max_by{ |key,value| value }
The compact form of this is :
hash = Hash.new(0)
gets.chomp.each_char { |c| hash[c] += 1 unless c == " " }
puts hash.max_by{ |key,value| value }
This returns the highest occurring character within a given string:
puts "give me a string"
characters = gets.chomp.split("").reject { |c| c == " " }
counts = Hash.new(0)
characters.each { |character| counts[character] += 1 }
print counts.max_by { |k, v| v }

Word Count returns an array (of arrays of the form [word, count]) representing the frequency of each word

str = 'put returns between paragraph put returns between paragraph put returns between paragraph'
def word_count(string)
resut= []
return result = string.split.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
end
def parse_word(word)
word.gsub!(/[^a-zA-Z0-9]/, " ")
word.downcase!
#yoo= word
end
result =word_count(str)
print result, "\n\n"
res2 = result.select { |pair| pair[1] > 1 } `#Error coming`
I am getting OutPut
**
OutPut
**
{"put"=>3, "returns"=>3, "between"=>3, "paragraph"=>3}
I need OutPut Like this
**
OutPut
**
{"put"=>3, "returns"=>3, "between"=>3, "paragraph"=>3}
and
put: 3
returns: 3
between: 3
but the main problem is that he gave us the code to do that but i cant able to understand it
I am not getting this what this code will do can anyone help me ...And modify it so it can work
The following processes the first paragraph of put returns ... Note that ss is an array of those words that occur at least twice in this paragraph.
nect = ss.select { |p| p[1] > 1 }
nect .sort.each do |key, count|
puts "#{key}: #{count}"
end
module WordCount
def self.word_count(s)
count_frequency(words_from_string(s))
end
def self.word_count_from_file(filename)
s = File.open(filename) { |file| file.read }
word_count(s)
end
def self.words_from_string(s)
s.downcase.scan(/[\w']+/)
end
def self.count_frequency(words)
counts = Hash.new(0)
for word in words
counts[word] += 1
end
# counts.to_a.sort {|a,b| b[1] <=> a[1]}
# sort by decreasing count, then lexicographically
counts.to_a.sort do |a,b|
[b[1],a[0]] <=> [a[1],b[0]]
end
end
end
def word_count(s)
WordCount.word_count(s)
end

Getting a nil:NilClass error with an array, not sure why

I am getting an error that '+' is undefined for nil:NilClass. I'm assuming this comes at
index[word] += 1
but am not sure why. I'm using 1.9.3.
If ayone could help, it would be appreciated!
Thanks
def most_common_words(text)
text_split = text.split(' ')
index = {}
text_split.each do |word|
puts index
puts word
if (index[word]
index[word] += 1 )
else(
index[word] = 1 )
end
end
index.to_a.sort[0..2]
The comment is barely correct.
It ignores the actual problem, which is that your malformed if statement.
The code works as written if you fix your syntax:
index = {}
%w[ohai kthx ohai].each do |word|
if index[word]
index[word] += 1
else
index[word] = 1
end
end
puts index.inspect
=> {"ohai"=>2, "kthx"=>1}
Or you could just provide a default value:
index2 = Hash.new(0)
%w[ohai kthx ohai].each do |word|
index2[word] += 1
end
puts index2.inspect
=> {"ohai"=>2, "kthx"=>1}
You should be able to simplify this code into fewer lines. It will make it look nicer and cleaner.
def most_common_words(text)
text_split = text.split(' ')
index = Hash.new(1)
text_split.each do |key, value|
puts "The key is #{key} and the value is #{value}"
index[key] += 1
end
end
index.to_a.sort[0..2]

How to get words frequency in efficient way with ruby?

Sample input:
"I was 09809 home -- Yes! yes! You was"
and output:
{ 'yes' => 2, 'was' => 2, 'i' => 1, 'home' => 1, 'you' => 1 }
My code that does not work:
def get_words_f(myStr)
myStr=myStr.downcase.scan(/\w/).to_s;
h = Hash.new(0)
myStr.split.each do |w|
h[w] += 1
end
return h.to_a;
end
print get_words_f('I was 09809 home -- Yes! yes! You was');
This works but I am kinda new to Ruby too. There might be a better solution.
def count_words(string)
words = string.split(' ')
frequency = Hash.new(0)
words.each { |word| frequency[word.downcase] += 1 }
return frequency
end
Instead of .split(' '), you could also do .scan(/\w+/); however, .scan(/\w+/) would separate aren and t in "aren't", while .split(' ') won't.
Output of your example code:
print count_words('I was 09809 home -- Yes! yes! You was');
#{"i"=>1, "was"=>2, "09809"=>1, "home"=>1, "yes"=>2, "you"=>1}
def count_words(string)
string.scan(/\w+/).reduce(Hash.new(0)){|res,w| res[w.downcase]+=1;res}
end
Second variant:
def count_words(string)
string.scan(/\w+/).each_with_object(Hash.new(0)){|w,h| h[w.downcase]+=1}
end
def count_words(string)
Hash[
string.scan(/[a-zA-Z]+/)
.group_by{|word| word.downcase}
.map{|word, words|[word, words.size]}
]
end
puts count_words 'I was 09809 home -- Yes! yes! You was'
This code will ask you for input and then find the word frequency for you:
puts "enter some text man"
text = gets.chomp
words = text.split(" ")
frequencies = Hash.new(0)
words.each { |word| frequencies[word.downcase] += 1 }
frequencies = frequencies.sort_by {|a, b| b}
frequencies.reverse!
frequencies.each do |word, frequency|
puts word + " " + frequency.to_s
end
This works, and ignores the numbers:
def get_words(my_str)
my_str = my_str.scan(/\w+/)
h = Hash.new(0)
my_str.each do |s|
s = s.downcase
if s !~ /^[0-9]*\.?[0-9]+$/
h[s] += 1
end
end
return h
end
print get_words('I was there 1000 !')
puts '\n'
You can look at my code that splits the text into words. The basic code would look as follows:
sentence = "Ala ma kota za 5zł i 10$."
splitter = SRX::Polish::WordSplitter.new(sentence)
histogram = Hash.new(0)
splitter.each do |word,type|
histogram[word.downcase] += 1 if type == :word
end
p histogram
You should be careful if you wish to work with languages other than English, since in Ruby 1.9 the downcase won't work as you expected for letters such as 'Ł'.
class String
def frequency
self.scan(/[a-zA-Z]+/).each.with_object(Hash.new(0)) do |word, hash|
hash[word.downcase] += 1
end
end
end
puts "I was 09809 home -- Yes! yes! You was".frequency

Resources