0

New to Ruby (coming from Python) and try to experiment this exercise: (mixed the array items by taking first, last in rotating fashion)
Expected Output to be - [1, 7, 2, 6, 3, 5, 4]. But I did not expect 'nil' at the end... The orig. array can contain even or odd size of numbers.

Can someone shed the light of this unexpected? Thanks in advance. [Updates - re-write the example from Ruby Cookbook p.162 Array ]

nums = (1..7).to_a   # [1, 2, 3, 4, 5, 6, 7] 

mixed = []
#middle = nums.length / 2

#index = 0

until nums.empty?
  mixed << nums.shift().  #get 1st element out
  mixed << nums.pop()     #get last element out
  #index += 1
  
end

print mixed    # Got [1, 7, 2, 6, 3, 5, 4, nil]
7
  • 2
    Because after you shift the last value off you then pop, perhaps forgetting to see if there is any data left. Put left finger at the start of the array, right at the end. When you say shift move your left finger to the right; when you say pop move your right finger to the left. See? Commented May 12, 2021 at 13:08
  • Hey, @DaveNewton - can you elaborate a little more, or share the pointers? Thanks. Commented May 12, 2021 at 13:09
  • 1
    Irb output [].pop # => nil [].shift #=> nil So last step in your example will produce nil since you have 2 operations in one iteration Commented May 12, 2021 at 13:10
  • 1
    Also note that this could be tested in irb. Just don't use the loop; create your array, and unroll the loop "by hand". Commented May 12, 2021 at 13:19
  • 1
    In python this would've crashed with a "IndexError: pop from empty list". Not sure if I'd prefer if ruby did that instead of returning nil Commented May 12, 2021 at 13:41

2 Answers 2

3

What's happening is that the total num of elements in the array is odd so the last value is put into mixed on 'shift' and then there is no element left in the array. This will solve your issue:

nums = (1..7).to_a   # [1, 2, 3, 4, 5, 6, 7] 

mixed = []
#middle = nums.length / 2

#index = 0

until nums.empty?
  mixed << nums.shift()
  mixed << nums.pop() unless nums.empty?
  #index += 1
  
end

print mixed    # Got [1, 7, 2, 6, 3, 5, 4]

Another way is: If the num of elements is odd then run the loop till n-1 and then get the last element out using shift/pop (doesn't matter if you use shift or pop at the end, you will get the same element.)

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

1 Comment

Another way to avoid the boundary condition is to perform only one operation within the loop, i.e. cycling between shift and pop instead of attempting both.
1

The Cookbook method can be made non-destructive (avoid modifying nums) as follows.

def doit(nums)
  nums.size.times.map { |i| i.even? ? nums[i/2] : nums[-i/2] }
end
doit [1, 2, 3, 4, 5, 6, 7]
  #=> [1, 7, 2, 6, 3, 5, 4]
doit [1, 2, 3, 5, 6, 7]
  #=> [1, 7, 2, 6, 3, 5]

Here is another (non-destructive) way to do that.

def doit(nums)
  n = nums.size/2
  nums.first(n).zip(nums.last(n).reverse).flatten.tap do |a|
    a << nums[n] if nums.size.odd?
  end
end
doit [1, 2, 3, 4, 5, 6, 7]
  #=> [1, 7, 2, 6, 3, 5, 4]
doit [1, 2, 3, 5, 6, 7]
  #=> [1, 7, 2, 6, 3, 5]

2 Comments

Like these very much. Is this more idiomatic way?
I would say "no"; just different

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.