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 }
Related
I was creating a ruby programme that will calculate the frequency of each letter appearing in my text and will be return it as a Hash.
Below is my code:
class LetterHistogram
attr_reader :letters
attr_accessor :text
def initialize(t = "Hello World!")
#text = t
end
def display
calculateFrequencies
("A".."Z").each {|x| puts "#{x}: " + "*" * letters[x]}
end
private
attr_writer :letters
def calculateFrequencies
calcuFreq = String.new(text)
calcuFreq.upcase!.gsub!(/\W+/, '')
letters.clear
letters.default = 0
calcuFreq.each_char {|char| letters[char] += 1}
end
end
But I getting this error when I run the display method
enter image description here
What is the error means and how to solve it?
The main problem is that in calculateFrequencies you are using a not assigned variable: letters. So, when you call calculateFrequencies in display, letters = nil and calling .clear on nil returns the error.
This is a modified version of the code, using snake_case (which is the Ruby writing standard).
class LetterHistogram
attr_accessor :text
def initialize(t = "Hello World!")
#text = t
end
def display
calculate_frequencies.each { |letter, freq| puts "#{letter}: #{freq}"}
end
private
def calculate_frequencies
freq = #text.upcase.gsub!(/\W+/, '').each_char.with_object(Hash.new(0)) { |letter, freq| freq[letter] += 1 }
freq.sort_by{ |letter, freq| freq }.reverse # just to sort
end
end
Instantiating an object and calling .display on it:
senctence = LetterHistogram.new
senctence.display
#=> L: 3
#=> O: 2
#=> D: 1
#=> R: 1
#=> W: 1
#=> E: 1
#=> H: 1
How it works
For calculating the frequency I used a Hash populated by: https://ruby-doc.org/core-2.5.1/Enumerable.html#method-i-each_with_object
Printing out freq from calculate_frequencies you can see it:
#=> {"H"=>1, "E"=>1, "L"=>3, "O"=>2, "W"=>1, "R"=>1, "D"=>1}
Alternatively, if you want also not used letters, you can initialise the freq hash with all values to 0, then update the hash, something like this:
freq = ("A".."Z").each_with_object({}) { |letter, freq| freq[letter] = 0 }
"Hello World!".upcase.gsub!(/\W+/, '').each_char { |letter| freq[letter] += 1 }
#=> {"A"=>0, "B"=>0, "C"=>0, "D"=>1, "E"=>1, "F"=>0, "G"=>0, "H"=>1, "I"=>0, "J"=>0, "K"=>0, "L"=>3, "M"=>0, "N"=>0, "O"=>2, "P"=>0, "Q"=>0, "R"=>1, "S"=>0, "T"=>0, "U"=>0, "V"=>0, "W"=>1, "X"=>0, "Y"=>0, "Z"=>0}
Finally, to print out the istogram:
freq.each { |letter, freq| puts "#{letter}: " + "◼︎" * freq if freq > 0 }
#=> D: ◼︎
#=> E: ◼︎
#=> H: ◼︎
#=> L: ◼︎◼︎◼︎
#=> O: ◼︎◼︎
#=> R: ◼︎
#=> W: ◼︎
I have a question about mysterious 'e' characters appearing in my counts hash.
My initial approach was clunky and inelegant:
def letter_count(str)
counts = {}
words = str.split(" ")
words.each do |word|
letters = word.split("")
letters.each do |letter|
if counts.include?(letter)
counts[letter] += 1
else
counts[letter] = 1
end
end
end
counts
end
This approach worked, but I wanted to make it a little more readable, so I abbreviated it to:
def letter_count(str)
counts = Hash.new(0)
str.split("").each{|letter| counts[letter] += 1 unless letter == ""}
counts
end
This is where I encountered the issue, and fixed it by using:
str.split("").each{|letter| counts[letter] += 1 unless letter == " "} # added a space.
I don't understand why empty spaces were being represented by the letter 'e' or being counted at all.
I don't understand why empty spaces were being represented by the letter 'e' or being counted at all.
I can't duplicate the problem:
def letter_count(str)
counts = Hash.new(0)
str.split("").each{|letter| counts[letter] += 1 unless letter == ""}
counts
end
letter_count('a cat') # => {"a"=>2, " "=>1, "c"=>1, "t"=>1}
"empty spaces"? There's no such thing. A space is not empty; It's considered blank but not empty:
' '.empty? # => false
Loading the ActiveSupport extension:
require 'active_support/core_ext/object/blank'
' '.blank? # => true
spaces are valid characters, which is why they're being counted. You have to disallow them if you don't want them counted.
For reference, here's how I'd do it:
def letter_count(str)
str.chars.each_with_object(Hash.new(0)) { |l, h| h[l] += 1 }
end
letter_count('a cat') # => {"a"=>2, " "=>1, "c"=>1, "t"=>1}
A messier way would be:
def letter_count(str)
str.chars.group_by { |c| c }.map { |char, chars| [char, chars.count] }.to_h
end
Breaking that down:
def letter_count(str)
str.chars # => ["a", " ", "c", "a", "t"]
.group_by { |c| c } # => {"a"=>["a", "a"], " "=>[" "], "c"=>["c"], "t"=>["t"]}
.map { |char, chars| [char, chars.count] } # => [["a", 2], [" ", 1], ["c", 1], ["t", 1]]
.to_h # => {"a"=>2, " "=>1, "c"=>1, "t"=>1}
end
Ruby already has String#each_char which you could use.
def char_count(string)
counts = Hash.new(0)
string.each_char { |char|
counts[char] += 1
}
return counts
end
puts char_count("Basset hounds got long ears").inspect
# {"B"=>1, "a"=>2, "s"=>4, "e"=>2, "t"=>2, " "=>4, "h"=>1,
# "o"=>3, "u"=>1, "n"=>2, "d"=>1, "g"=>2, "l"=>1, "r"=>1}
As for why you're getting the wrong characters, are you sure you're passing in the string you think you are?
I have an Array-1 say
arr1 =['s','a','sd','few','asdw','a','sdfeg']
And a second Array
arr2 = ['s','a','d','f','w']
I want to take arr1 and sort the frequency of letters by inputting arr2 with result
[s=> 4, a=> 2, d => 3] So on and so forth.
As far as I can muddle around.. Nothing below works, Just my thoughts on it?
hashy = Hash.new
print "give me a sentance "
sentance = gets.chomp.downcase.delete!(' ')
bing = sentance.split(//)
#how = sentance.gsub!(/[^a-z)]/, "") #Remove nil result
#chop = how.to_s.split(//).uniq
#hashy << bing.each{|e| how[e] }
#puts how.any? {|e| bing.count(e)}
#puts how, chop
bing.each {|v| hashy.store(v, hashy[v]+1 )}
puts bing
Thank you for your time.
I assumed that you want to count all letters in the sentence you put in, and not array 1. Assuming that, here's my take on it:
hashy = Hash.new()
['s','a','d','f','w'].each {|item| hashy[item.to_sym] = 0}
puts "give me a sentence"
sentence = gets.chomp.downcase.delete!(' ')
sentence_array = []
sentence.each_char do |l|
sentence_array.push(l)
end
hashy.each do |key, value|
puts "this is key: #{key} and value #{hashy[key]}"
sentence_array.each do |letter|
puts "letter: #{letter}"
if letter.to_sym == key
puts "letter #{letter} equals key #{key}"
value = value + 1
hashy[key] = value
puts "value is now #{value}"
end
end
end
puts hashy
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
How can I have a hash of hash of hash?
My test returns
undefined method `[]' for nil:NilClass (NoMethodError)
Any tips?
found = Hash.new()
x = 1;
while x < 4 do
found[x] = Hash.new()
y = 1
while y < 4 do
found[x][y] = Hash.new()
found[x][y]['name1'] = 'abc1'
found[x][y]['name2'] = 'abc2'
found[x][y]['name3'] = 'abc3'
y += 1
end
x += 1
end
found.each do |k, v, y|
puts "k : #{k}"
puts " : #{v[y['name1']]}"
puts " : #{v[y['name2']]}"
puts " : #{v[y['name3']]}"
puts
end
I think you want something like this:
First of all create the data structure. You want nested hashes so you need to define default values for each hash key.
found = Hash.new do |hash,key|
hash[key] = Hash.new do |hash,key|
hash[key] = Hash.new
end
end
Run the search
(1..3).each do |x|
(1..3).each do |y|
found[x][y]['name1'] = 'abc1'
found[x][y]['name2'] = 'abc1'
found[x][y]['name3'] = 'abc1'
end
end
Then display the results
found.each do |x, y_hash|
y_hash.each do |y, name_hash|
name_hash.each do |name, value|
puts "#{x} => #{y} => #{name} => #{value}"
end
end
end
The way you build the hash seems to be functional. What probably causes the error is this loop:
found.each do |k, v, y|
Hash#each yields key/value pairs, so y will be assigned nil, thus causing the error two lines below. What you probably meant is a nested loop like
found.each do |x, h1|
h1.each do |y, h2|
puts h2['name1']
end
end
You should also be aware that you can write these kinds of counting loops more concisely in Ruby:
found = Hash.new { |h,k| h[k] = {} }
1.upto(3) do |x|
1.upto(3) do |y|
found[x][y] = {
'name1' => 'abc1',
'name2' => 'abc2',
'name3' => 'abc3',
}
end
end