0

Given an array in Ruby:

numbers = [1, 2, 3, 4]

I'm confused about the result of what happens when modifying the array while iterating over the array:

numbers.each do |number|
  p number
  numbers.shift(1)
end

The output I get when running the code is 1 and 3. The 1 makes perfect sense for the first loop, but I can't seem to figure out why the program is returning a 3.

I modified the original code so I could see what is occurring at each step along the way, and included the hash marks "----" to know when each loop is finished running:

numbers = [1, 2, 3, 4]
numbers.each_with_index do |number, index|
  p "Array is #{numbers} and index is #{index}"
  p "Number is #{number}"
  numbers.shift(1)
  p "The Array after shift is now #{numbers}, number is #{number}, and the index is now #{index}"
  p "----"
end

Through the first loop, shift successfully removes the 1 from the original array and the array becomes [2,3,4], while "number" is 1. But on the second loop through, instead of "number" returning 2 it returns 3.

The only way I think it makes sense is if the index itself must be determining which item to remove from the array. When the index is 0 it's removing the array item at index 0 from the array, and when the index is 1 it's removing array item at index 1 from the updated array. Then the loop stops when the index is 2 because the array no longer has an item at index 2.

This is a question as part of the Launch School course. I can't figure out why the code returns what it does. I also tried inserting binding.pry both before and after the numbers.shift(1) is called, but it didn't seem to clarify anything to me.

2
  • Tip: Try unshift(1) or numbers.reverse! Commented Feb 14, 2020 at 0:42
  • Try this: numbers.each do |number|; puts "number=#{number}, numbers=#{numbers}"; puts "numbers.shift returns #{numbers.shift(1)}"; puts "numbers after shift=#{numbers}"; end. This displays the following: number=1, numbers=[1, 2, 3, 4]; numbers.shift returns [1]; numbers after shift=[2, 3, 4]; number=3, numbers=[2, 3, 4]; numbers.shift returns [2]; numbers after shift=[3, 4] and returns [3, 4]. Then numbers #=> [3, 4]. Note that Array#each returns it's receiver, the array numbers. Commented Feb 14, 2020 at 2:04

2 Answers 2

2
numbers = [1, 2, 3, 4]

Okay, here we go with

numbers.each do |number|
  # do stuff
end

First number

p number # 1, we are on the first element of [1, 2, 3, 4]
numbers.shift(1) # numbers is now [2, 3, 4]

Second number

p number # 3, we are on the second element of [2, 3, 4]
numbers.shift(1) # numbers is now [3, 4]

Third number – wait, there is no third element of [3, 4], we’re done.

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

1 Comment

Ahhh, that makes sense! Thanks!
1

do not look at it as values but rather as an address.
Enumerator(or the Iteration) follows the next address pointed by the current one.

1st loop:
  1. number points to the first address (with value 1)
  2. shifting array making the current 2nd address the 1st, therefore the value 2 is now on the 1st address.

2nd loop:
  1. number points to the 2nd address (with value 3)
  2. shifting array making the current 2nd address the 1st

3rd loop:
  ※same logic until 'StopIteration' is met

In regards to 'shift', it will just remove the 1st item without overthinking.

reference: https://apidock.com/ruby/Enumerator/next_values

Comments

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.