joh-mue joh-mue - 5 months ago 12
Ruby Question

Why is my block only executed once?

Why is it that:

array = (1..20).to_a
array.index.each_slice(5) do |slice|
puts slice.inspect
end


returns:

[1, 2, 3, 4, 5]
[6, 7, 8, 9, 10]
[11, 12, 13, 14, 15]
[16, 17, 18, 19, 20]


while:

other_array = []
array = (1..20).to_a
array.index.each_slice(5) do |slice|
puts slice.inspect
other_array.push(1)
end


returns only:

[1, 2, 3, 4, 5]


How does
other_array.push(1)
breaks the execution of the block? An obvious conclusion would be that I cannot access variables that are not in the scope of the block, but why is that?

Answer

I found the solution in the array documentation. I wondered why you used the index function for the array when it seems like you just want to iterate over the array. For this you can use array.each_slice without invoking index. Index says the following: http://ruby-doc.org/core-2.2.0/Array.html#method-i-index

Returns the index of the first object in ary such that the object is == to obj.

If a block is given instead of an argument, returns the index of the first object for which the block returns true. Returns nil if no match is found

So your code evaluates the block and checks if the result is true. In the first example you do only a puts which returns nil. Nil is false.
The second example returns an object of an array containing a single 1.
In ruby every condition is true, if it is not false or nil. You can see this here:

if nil
   puts "foo"
end
=> nil

other_array = [1]
if other_array
  puts "foo"
end
=> "foo"

So the block in your second example returns something not-false so it will not run again, because it found a "valid" result.

For the return, you maybe should know that ruby returns the last expression in any scope, if no other return is given. So it returns other_array. If you don't want to reformat your code you could to the following:

other_array = []
array = (1..20).to_a
array.index.each_slice(5) do |slice|
  puts slice.inspect
  other_array.push(1)
  nil 
end

This will force the block to return nil and the iteration will work.