8/8 - Help confirming I understand a few things


#1

Looking for some help checking my understanding of the items below. Especially #3 which has me a confused. Much thanks in advance!!

1)
words.each do |word|
frequencies[word] += 1
end

The above code iterates over the words array, word is placed in the frequencies hash with a value of 1 initially, if the key already exists the value is incremented, if not a new key-value pair is created.

2) frequencies = frequencies.sort_by{|word,count| count}

The above code uses the sort_by method over the frequencies hash. As frequencies is a hash, we need two variables to iterate over, the count following |word, count| tells sort_by what we're ... sorting by, and then stores the information in frequencies which is no longer a hash but an array of arrays.

3)
frequencies.each do |word,count|
count = count.to_s
puts "#{word} #{count}"
end

The above is where I get a bit confused. I thought frequencies was now an array of arrays, so I was thinking I'd have to use the method covered in an earlier lesson (an iterator within an iterator).


Sort_by changes frequencies to a 2D array so how does a hash each syntax |key,value| work later?
#2
  1. Yes, that is correct.
  2. No, it is still a hash, just sorted by count.
  3. Because frequencies is still a hash, you can do it this way.

#3

Sorry, I'm also confused here. To make sure the value of the "frequencies" variable changes the type to array instead of hash I've put the "frequencies.class" before calling ".sort_by" and after.
And it's HASH before and ARRAY after!)
If that's an array, how could we get the key and value to variables |word,count| instead of just getting the first "sub_array" to the word variable and nil to other (as I would have expected with 2D array)?


#4

Hey @csswhiz88972

When using the sort_by method on the hash object, what you get in return is a 2D array.

If you use the .inspect function on the frequencies variable before and after, you can get a more detailed look of the values.

Using an example string "Testing of multiples of arrays"

Executing frequencies.inspect before the sort will return
{"Testing"=>1, "of"=>2, "multiples"=>1, "arrays"=>1}

But if you do the same inspect afterwards, you'll see it's now an array instead
[["Testing", 1], ["arrays", 1], ["multiples", 1], ["of", 2]]

When then executing the .each block on the 2D array with two parameters, it seems that Ruby knows to assign the first variable to the first value of the array, the second variable to the second value and so forth.

I'm afraid I don't know the precise logic as to how Ruby does it though, but it does seem to be a bit related how blocks in Ruby doesn't require precise number of arguments.

I've pasted an example below that I tried out, and it might demonstrate a bit on it works.
The comments below each statement is what the output was.

my_array = [[1,"Hello", false],[2,"Hey there", true],[3, "Howdy"]]

#I'm using 'p' instead of puts, since it's the same as running .inspect on the variable sent

p my_array
#[[1, "Hello", false], [2, "Hey there", true], [3, "Howdy"]]
#Here's the entire 2D array as it's been made above

my_array.each {|x| p x}
#[1, "Hello", false]
#[2, "Hey there", true]
#[3, "Howdy"]
#Here the .each will pass along each individual sub-array to the block, and print those out

my_array.each {|x,y,z| p "#{y}, #{x} - #{z}"}
#"Hello, 1 - false"
#"Hey there, 2 - true"
#"Howdy, 3 - "
#Here each element of the sub-array is passed to x, y and z. As you might notice in the last example, nothing is printed after the '-', since the original array only has two values in the third sub-array.

#5

Thank you very much for the detailed explanation, distrattos! Don't know why I was so sure that array behaves in a different way than hash with ".each" method)

Regarding the question 3) the confusing thing is that in "Iterating Over Multidimensional Arrays" we've actually iterated over each sub-array:

s = [["ham", "swiss"], ["turkey", "cheddar"], ["roast beef", "gruyere"]]
s.each do |sub_array|
sub_array.each do |x|
puts "#{x}"
end
end

However, knowing that each sub_array has 2 elements, we could just:

s = [["ham", "swiss"], ["turkey", "cheddar"], ["roast beef", "gruyere"]]
s.each do |x ,y|
puts x
puts y
end

So, looks like both variants are OK. But it is more flexible to iterate over each sub_array as we may not know the exact number of elements in each sub_array. For instance, if we get the input from the user.