1

I expected row to be [0, 0, 0, 0] and row_index to be nil in the following:

img_array = [
  [0, 0, 0, 0],
  [0, 0, 0, 0],
  [0, 0, 0, 0],
  [0, 1, 0, 0],
  [0, 0, 0, 0],
  [0, 0, 0, 0]
]

img_array.each do |row, row_index|
  ...
end

Actually, row is 0 and row_index is 0. Can anyone explain this?

2
  • Cannot be reproduced. It is not even valid. Commented Nov 19, 2015 at 5:05
  • @sawa It runs fine for me in ruby-2.2.2, and produces the described result. Commented Nov 19, 2015 at 12:42

2 Answers 2

3
img_array = [
  [0, 1, 2, 3],
  [4, 5, 6, 7],
  [8, 9, 0, 1]
]

We have:

enum = img_array.each
  #=> #<Enumerator: [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 0, 1]]:each> 

Array#each therefore creates an enumerator that is an instance of the class Enumerator. The method Enumerator#each passes each element of enum to the block and assigns the block variables:

enum.each { |row, row_index| puts "row=#{row}, row_index=#{row_index}" }
  # row=0, row_index=1
  # row=4, row_index=5
  # row=8, row_index=9

We can see what each of the elements of enum are by using the method Enumerator#next:

enum.next #=> StopIteration: iteration reached an end

Whoops! I forgot to reset the enumerator:

enum.rewind
  #=> #<Enumerator: [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 0, 1]]:each>

enum.next #=> [0, 1, 2, 3] 
enum.next #=> [4, 5, 6, 7] 
enum.next #=> [8, 9, 0, 1] 
enum.next #=> StopIteration: iteration reached an end

Alternatively, we could convert the enumerator to an array (no need to rewind):

enum.to_a #=> [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 0, 1]]

When the element ([0, 1, 2, 3]) is passed to the block, the block variables are assigned as follows:

row, row_index = [0, 1, 2, 3]            #=> [0, 1, 2, 3] 
row                                      #=> 0
row_index                                #=> 1

If the variables were |row, row_index, col_index|, the assignment would be:

row, row_index, col_index = [0, 1, 2, 3] #=> [0, 1, 2, 3] 
row                                      #=> 0
row_index                                #=> 1
col_index                                #=> 2

If they were |row, row_index, *rest|, it would be:

row, row_index, *rest = [0, 1, 2, 3]     #=> [0, 1, 2, 3] 
row                                      #=> 0
row_index                                #=> 1
rest                                     #=> [2, 3]

This is called parallel (or multiple) assignment. You can read up on the rules for such assignments at the link I've given.

Sign up to request clarification or add additional context in comments.

2 Comments

Excellent answer. Form this I understood what was OP's question actually :D
Cary this makes total sense! Thank you for your clear, in-depth input. I have a better understanding of how enumerators work now.
1

The method .each() on array in Ruby will call your block with only one argument. So if you write your code like this:

img_array.each do |row, row_index|
  # do something here
end

It will equivalent to this:

img_array.each do |element|
  row, row_index = element
  # and now
  # row = element[0]
  # row_index = element[1]
end

I made an example that easier to understand

img_array = [
  ['a', 'b', 'd', 'e'],
  ['f', 'g', 'h', 'j']
]

img_array.each do |row, row_index|
    p row
    p row_index
end

And the result will be:

"a"
"b"
"f"
"g"

Run it online here: https://ideone.com/ifDaVZ

2 Comments

I checked it out, and it runs just as you said. Thank you for you explanation. It really helped me out!
@EricOrel You're welcome. If this answer solve your problem, please mark this answer as accepted so others can find it easier.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.