ruby blocks working code academy question - ruby

This query is about creating a histogram where user gives an input and we calculate the number of times a word has occurred
I am not able to understand the solution
input = "Hello how are you? I am fine how are you"
text = input.to_s
words = text.split
frequencies = Hash.new(0)
words.each do |word|
frequencies[word] += 1
end
p frequencies
Output
{"Hello"=>1, "how"=>2, "are"=>2, "you?"=>1, "I"=>1, "am"=>1, "fine"=>1, "you"=>1}
[Finished in 527ms]
In the above code we are calculating the frequency of each word. but where are we storing it in the frequencies hash?
which part of the code is doing that?
If i include a print statement inside the block it only gives the frequency.. so how is the word itself getting stored..
My apologies in advance if this is a silly question but i am not able to understand how the assignment is happening at the back end-- if we print it (inside the block), its displaying only the frequency..
Thanks in advance for helping out..

Adding the word to the hash and increasing the counter happens in the line
frequencies[word] += 1
and that only works because when the key does not exist yet then 0 is returned because of how the hash was defined in this line
frequencies = Hash.new(0)
Btw when you take advantage of Enumerable#tally then you can solve the whole problem in just only line:
input.split.tally
#=> {"Hello"=>1, "how"=>2, "are"=>2, "you?"=>1, "I"=>1, "am"=>1, "fine"=>1, "you"=>1}

Related

Can someone explain me what this line of ruby code does?

I'm a beginner in ruby and found this example on the Odin project about the reduce method, but in line 7 it puts the result variable again, can someone explain me What's the use of putting the result variable?
Thank you in advance!
votes = ["Bob's Dirty Burger Shack", "St. Mark's Bistro", "Bob's Dirty Burger Shack"]
votes.reduce(Hash.new(0)) do |result, vote|
puts "result is #{result} and votes is #{vote}"
puts "This is result [vote]: #{result[vote]}"
result[vote] += 1
result #this part I don't understand
end
They're using the reduce(initial_operand) {|memo, operand| ... } version.
memo is a thing to collect the result. The block has to pass that along to the next iteration. For example, if you wanted to sum up a list of numbers...
(1..4).inject do |sum, element|
p "Running sum: #{sum}; element: #{element}"
# This is passed along to the next iteration as sum.
sum + element
end
Instead of using the default memo, which would be the first element, they've used Hash.new(0) to count the votes. Each iteration counts the votes, and then passes the result has to the next iteration.
# result starts as Hash.new(0)
votes.reduce(Hash.new(0)) do |result, vote|
# This prints the state of the voting and the current choice being tallied.
puts "result is #{result} and votes is #{vote}"
# This displays how many votes there are for this choice at this point
# before this choice is tallied.
puts "This is result [vote]: #{result[vote]}"
# This adds the vote to the tally.
result[vote] += 1
# This passes along the tally to the next iteration.
result
end
If you don't want to print on each iteration, use tally instead.
result = votes.tally

Swap adjacent elements in array

I am trying to build a method in Ruby that will take in a string that has been split into an array of letters and then iterate through the array, swapping the element at index n with that at index n+1. The method will then join the new array into a string and push it to another array.
Here is an example of what I am looking to do:
string = "teh"
some_method(string)
some ruby magic here
array << new_string
end
Expected output:
["eth", "the"]
This is for a spell checker program I am writing for school. The method will check if letters in a misspelled word are swapped by checking to see if the output array elements are in the dictionary. If they are, it will return the word with that is most likely the correct word. I haven't had any luck finding articles or documentation on how to build such a method in ruby or on an existing method to do this. I've been tinkering with building this method for awhile now but my code isn't behaving anything like what I need. Thanks in advance!
As #Sergio advised, you want to use parallel assignment for this:
def reverse_em(str)
(0...str.size-1).map do |i|
s = str.dup
s[i], s[i+1] = s[i+1], s[i]
s
end
end
candidates = reverse_em "alogrithm"
#=> ["laogrithm", "aolgrithm", "algorithm", "alorgithm",
# "alogirthm", "alogrtihm", "alogrihtm", "alogritmh"]
dictionary_check(candidates)
#=> algorithm
# al·go·rithm
# noun \ˈal-gə-ˌri-thəm\
# a set of steps that are followed in order to solve a
# mathematical problem or to complete a computer process
Without splitting it into arrays then joining to new arrays (because that doesn't seem necessary):
def some_method(string)
swapped_strings = []
(0...string.size-1).each do |i|
temp_string = string.dup
temp_string[i], temp_string[i+1] = temp_string[i+1], temp_string[i]
swapped_strings << temp_string
end
swapped_strings
end

Coderbyte Second Great Low - code works but is rejected

I'm currently working through the Coderbyte series to get better at Ruby programming. Maybe this is just a bug in their site (I don't know), but my code works for me everywhere else besides on Coderbyte.
The purpose of the method is to return the 2nd smallest and the 2nd largest elements in any inputted array.
Code:
def SecondGreatLow(arr)
arr=arr.sort!
output=[]
j=1
i=(arr.length-1)
secSmall=''
secLarge=''
while output.length < 1
unless arr.length <= 2
#Get second largest here
while (j<arr.length)
unless arr[j]==arr[j-1]
unless secSmall != ''
secSmall=arr[j]
output.push(secSmall)
end
end
j+=1
end
#get second smallest here
while i>0
unless arr[i-1] == arr[i]
unless secLarge != ''
secLarge=arr[i-1]
output.push(secLarge)
end
end
i-=1
end
end
end
# code goes here
return output
end
# keep this function call here
# to see how to enter arguments in Ruby scroll down
SecondGreatLow(STDIN.gets)
Output
Input: [1,2,3,100] => Output: [2,3] (correct)
Input: [1,42,42,180] => Output: [42,42] (correct)
Input: [4,90] => Output: [90,4] (correct)
The problem is that I'm awarded 0 points and it tells me that my output was incorrect for every test. Yet, when I actually put any inputs in, it gives me the output that I expect. Can someone please assist with what the problem might be? Thanks!
Update
Thanks to #pjs answer below, I realized this could be done in just a few lines:
def SecondGreatLow(arr)
arr=arr.sort!.uniq
return "#{arr[1]} #{arr[-2]}"
end
# keep this function call here
# to see how to enter arguments in Ruby scroll down
SecondGreatLow(STDIN.gets)
It's important to pay close attention to the problem's specification. Coderbyte says the output should be the values separated by a space, i.e., a string, not an array. Note that they even put quotes around their "Correct Sample Outputs".
Spec aside, you're doing way too much work to achieve this. Once the array is sorted, all you need is the second element, a space, and the second-to-last element. Hint: Ruby allows both positive and negative indices for arrays. Combine that with .to_s and string concatenation, and this should only take a couple of lines.
If you are worried about non-unique numbers for the max and min, you can trim the array down using .uniq after sorting.
You need to check condition for when array contains only two elements. Here is the complete code:
def SecondGreatLow(arr)
arr.uniq!
arr.sort!
if arr.length == 2
sec_lowest = arr[1]
sec_greatest = arr[0]
else
sec_lowest = arr[1]
sec_greatest = arr[-2]
end
return "#{sec_lowest} #{sec_greatest}"
end

Nested Data Structures: Why is my Hash turning into an Array of an Array?

For an assignment I am working on, I'm trying to sort words in a piece of text by frequency of words in the text. I have a function that almost accomplishes what I'd like to do but not quite. Below is my code:
require 'pry'
def top_words(words)
word_count = Hash.new(0)
words = words.split(" ")
words.each { |word| word_count[word] += 1 }
word_count = word_count.sort_by do |words, frequencies|
frequencies
end
binding.pry
word_count.reverse!
word_count.each { |word, frequencies| puts word + " " + frequencies.to_s }
end
words = "1st RULE: You do not talk about FIGHT CLUB.
2nd RULE: You DO NOT talk about FIGHT CLUB.
3rd RULE: If someone says 'stop' or goes limp, taps out the fight is over.
4th RULE: Only two guys to a fight.
5th RULE: One fight at a time.
6th RULE: No shirts, no shoes.
7th RULE: Fights will go on as long as they have to.
8th RULE: If this is your first night at FIGHT CLUB, you HAVE to fight."
For some reason, the sort_by method above my binding.pry is changing the structure of my Hash into an array of an array. Why?
What I'd like to do is to sort the words within a hash and then grab the top three words from the Hash. I've yet to figure out how to do this but I'm pretty sure I can do this once I've sorted the array of an array problem.
Now, I suppose I could grab them using .each and array[0].each { |stuff| puts stuff[0] + stuff[1] } but I don't think that is the most efficient way. Any suggestions?
For some reason, the sort_by method above my binding.pry is changing the structure of my Hash into an array of an array. Why?
Explanation is below :
sort_by { |obj| block } → array method give always array.
The current implementation of sort_by generates an array of tuples containing the original collection element and the mapped value. This makes sort_by fairly expensive when the keysets are simple.
Now in your case word_count is a Hash object, thus sort_by is giving you like - [[key1,val],[key2,val2],..]. This is the reason you are getting array of array.
What I'd like to do is to sort the words within a hash and then grab the top three words from the Hash. I've yet to figure out how to do this but I'm pretty sure I can do this once I've sorted the array of an array problem.
Yes, possible.
sorted_array_of_array = word_count.sort_by do |words, frequencies| frequencies }
top_3_hash = Hash[ sorted_array_of_array.last(3) ]
I would write the code as below :
def top_words(words)
# splitting the string words on single white space to create word array.
words = words.split(" ")
# creating a hash, which will have key as word and value is the number of times,
# that word occurred in a sentence.
word_count = words.each_with_object(Hash.new(0)) { |word,hash| hash[word] += 1 }
# sorting the hash, to get a descending order sorted array of array
sorted_array_of_array = word_count.sort_by { |words, frequencies| frequencies }
# top 3 word/frequency is taken from the sorted list. Now reading them from last
# to show the output as first top,second top and so on..
sorted_array_of_array.last(3).reverse_each do |word, frequencies|
puts "#{word} has #{frequencies}"
end
end

Why is the method deleting the element inside my array?

I'm trying to create my own .sort method as an exercise in a ruby book, using recursion, and for some reason they haven't taught me the spaceship operator yet. My code works to get the smallest value - apple - and puts it in the sorted array, and it even repeats using the recursion, and resets the array to repeat the process to add the second smallest word. The problem is for some reason it removes the smallest word -apple- and I can't figure out why. I know where I think - in the else myArray.length == 1 statement when I pop the element off the array, but why is it removing from the sortedArray too?
sortedArray ends up with value apple, then when it does recursion it SHOULD be sortedArray = ['apple', 'banana' …] but it removes apple, then it removes banana etc… until I end up with sortedArray = ['quincy']
I have tried moving my arrays to multiple places, and I've tried adding to the sortedWords array in multiple places but it is always deleting or resetting the sortedWords array.
It looks like I'm really close since I've got the alphabetizing working. How do I get it to add all the items to the sortedWords array?
ArrayofWords = ['cat', 'dog', 'bat', 'elephant', 'apple', 'banana', 'quincy', 'boo']
# Why is it deleting, or replacing my sortedWords array? If you run this code you will notice that the sortedWords array
# is giving me the smallest word in the array, but then I add the recursive part, and somehow the previous smallestword
#gets deleted... but I have never in any part of my code say delete or replace the sorted array...
def sortTheArray myArray
unsortedWords = []
sortedWords = []
smallestValue = ''
while myArray.length != 0
if myArray.first < myArray.last
unsortedWords.push(myArray.last)
myArray.pop
elsif myArray.first > myArray.last
unsortedWords.push(myArray.first)
myArray.delete_at(0)
else myArray.length == 1
sortedWords.push(myArray.first)
myArray.pop # This is my problem area I think???
end # if else
#puts 'sorted words'
#puts sortedWords
#puts 'unsortedWords'
#puts unsortedWords
end # while
puts 'sorted words'
puts sortedWords
puts 'unsortedWords'
puts unsortedWords
myArray = unsortedWords
while myArray.length > 0
sortTheArray myArray
end #while
end # sortTheArray
sortTheArray ArrayofWords
most of those puts's are not necessary, I was just trying to figure out where the problem was.
You've got numerous problems with your code. For example, you seem to want to accumulate sorted words across invocations of this method, but you reinitialize sorted_words to [] at the start of the method block.
I would suggest first trying to express your recursive solution in English as simply as possible and then seek to implement it.
For example, the following is an approach which seems to be in line with what you are trying to do:
def sorted_array(array)
lowest_value prepended to the sorted_value of the array with the lowest_value removed
end
I'm sharing the above because it appears that you're new to Ruby and just implementing the above in an idiomatic fashion will be a good challenge.

Resources