5
votes

I've recently started coding Ruby and I'm having a mis-understanding with block parameters. Take the following code for example:

h = { # A hash that maps number names to digits
:one => 1, # The "arrows" show mappings: key=>value
:two => 2 # The colons indicate Symbol literals
}
h[:one] # => 1. Access a value by key
h[:three] = 3 # Add a new key/value pair to the hash
h.each do |key,value| # Iterate through the key/value pairs
  print "#{value}:#{key}; " # Note variables substituted into string
end # Prints "1:one; 2:two; 3:three; "

I understand the general hash functionality, however I don't understand how value and key are set to anything. They are specified as parameters in the block, but the hash is never associated in any way with these parameters.

5
When it says h.each do |key,value| the association of key,value to h for the entire do block is defined on that line. The names key and value could be whatever you like.Fareesh Vijayarangam
If h.each looks too magical for then you could always use h.each_pair.Jonas Elfström
Ruby comes with IRB, which you can run from the command-line using irb. It lets you easily start playing with variables and assignments. It's a great tool when you want to learn these sorts of things.the Tin Man
You may want to accept answers, if they solve your problems. They're the tick icon next to each question.Andrew Grimm

5 Answers

6
votes

The hash (h) is associated with the loop due to you calling h.each rather than calling each on something else. It's effectively saying, "For each key/value pair in h, let the key iteration variable be the key, let the value iteration variable be the value, then execute the body of the loop."

If that doesn't help, have a look at this page on each... and if you can explain more about which bit you're finding tricky, we may be able to help more. (Well, others may be able to. I don't really know Ruby.)

6
votes

This is the Ruby block (Ruby's name for an anonymous function) syntax. And key, value are nothing but the arguments passed to the anonymous function.

Hash#each takes one parameter: A function which has 2 parameters, key and value.

So if we break it down into parts, this part of your code: h.each, is calling the each function on h. And this part of your code:

do |key, value| # Iterate through the key/value pairs
  print "#{value}:#{key}; " # Note variables substituted into string
end # Prints "1:one; 2:two; 3:three; 

is the function passed to each as an argument and key, value are arguments passed to this function. It doesn't matter what you name them, first argument expected is key and second argument expected is value.

Lets draw some analogies. Consider a basic function:

 def name_of_function(arg1, arg1)
   # Do stuff
 end

 # You'd call it such:
 foo.name_of_function bar, baz # bar is becomes as arg1,  baz becomes arg2 

 # As a block:

 ooga = lambda { |arg1, arg2|
   # Do stuff
 }

 # Note that this is exactly same as:

 ooga = lambda do |arg1, arg2|
   # Do stuff
 end

 # You would call it such:

 ooga.call(bar, baz) # bar is becomes as arg1,  baz becomes arg2

So your code can also be written as:

print_key_value = lambda{|arg1, arg2| print "#{arg1}:#{arg2}"}
h = { 
  :one => 1,
  :two => 2
}

h.each &print_key_value

There are multiple ways in which the code inside a block can be executed:

  yield
  yield key, value # This is one possible way in which Hash#each can use a block
  yield item

  block.call
  block.call(key, value) # This is another way in which Hash#each can use a block
  block.call(item)
1
votes

The hash is indeed associated with these parameters because you call h.each to iterate over the hash:

h.each <- here's the link you are missing

Perhaps it's easier for you if you start with an array instead:

a = [1,2,3]
a.each do |v|
  puts v
end

and play around with this first (each, each_with_index, ...)

0
votes

when you call h.each, that's when you say that this is this specific h hash that you want to use for this each iteration. Hence when you do that the value and key variables are assigned to the values in your hash, one by one.

0
votes

I think the question is about the variable names. The names have no significance. Only the order matters. Within |...| inside each {...}, the key and the value are given in that order. Since its natural to assign the variable name key to key and value to value, you often find it done like that. In fact, it can be anything else.

each{|k, v| ...}  # k is key, v is value
each{|a, b| ...}  # a is key, b is value

or even misleadingly:

each{|value, key| ...} # value is key, key is value