What's the 'Ruby way' to iterate over two arrays at once

RubyArraysIterator

Ruby Problem Overview


More of a syntax curiosity than a problem to solve...

I have two arrays of equal length, and want to iterate over them both at once - for example, to output both their values at a certain index.

@budget = [ 100, 150, 25, 105 ]
@actual = [ 120, 100, 50, 100 ]

I know that I can use each_index and index into the arrays like so:

@budget.each_index do |i|
  puts @budget[i]
  puts @actual[i]
end

Is there a Ruby way to do this better? Something like this?

# Obviously doesn't achieve what I want it to - but is there something like this?
[@budget, @actual].each do |budget, actual|
  puts budget
  puts actual
end

Ruby Solutions


Solution 1 - Ruby

>> @budget = [ 100, 150, 25, 105 ]
=> [100, 150, 25, 105]
>> @actual = [ 120, 100, 50, 100 ]
=> [120, 100, 50, 100]

>> @budget.zip @actual
=> [[100, 120], [150, 100], [25, 50], [105, 100]]

>> @budget.zip(@actual).each do |budget, actual|
?>   puts budget
>>   puts actual
>> end
100
120
150
100
25
50
105
100
=> [[100, 120], [150, 100], [25, 50], [105, 100]]

Solution 2 - Ruby

Use the Array.zip method and pass it a block to iterate over the corresponding elements sequentially.

Solution 3 - Ruby

There is another way to iterate over two arrays at once using enumerators:

2.1.2 :003 > enum = [1,2,4].each
 => #<Enumerator: [1, 2, 4]:each> 
2.1.2 :004 > enum2 = [5,6,7].each
 => #<Enumerator: [5, 6, 7]:each> 
2.1.2 :005 > loop do
2.1.2 :006 >     a1,a2=enum.next,enum2.next
2.1.2 :007?>   puts "array 1 #{a1} array 2 #{a2}"
2.1.2 :008?>   end
array 1 1 array 2 5
array 1 2 array 2 6
array 1 4 array 2 7

Enumerators are more powerful than the examples used above, because they allow infinite series, parallel iteration, among other techniques.

Solution 4 - Ruby

In addition to a.zip(b).each{|x,y| } as others have said, you can also say [a,b].transpose.each{|x,y| }, which strikes me as a tiny bit more symmetrical. Probably not as fast, though, since you're creating the extra [a,b] array.

Solution 5 - Ruby

Related to the original question, for iterating over arrays of unequal length where you want the values to cycle around you can use

[1,2,3,4,5,6].zip([7,8,9].cycle)

and Ruby will give you

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

This saves you from the nil values that you'll get from just using zip

Solution 6 - Ruby

Simply zipping the two arrays together works well if you are dealing with arrays. But what if you are dealing with never-ending enumerators, such as something like these:

enum1 = (1..5).cycle
enum2 = (10..12).cycle

enum1.zip(enum2) fails because zip tries to evaluate all the elements and combine them. Instead, do this:

enum1.lazy.zip(enum2)

That one lazy saves you by making the resulting enumerator lazy-evaluate.

Solution 7 - Ruby

How about compromising, and using #each_with_index?

include Enumerable 

@budget = [ 100, 150, 25, 105 ]
@actual = [ 120, 100, 50, 100 ]

@budget.each_with_index { |val, i| puts val; puts @actual[i] }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnfmView Question on Stackoverflow
Solution 1 - RubyJohn La RooyView Answer on Stackoverflow
Solution 2 - RubyAnuragView Answer on Stackoverflow
Solution 3 - RubyDonatoView Answer on Stackoverflow
Solution 4 - RubyPaul A JungwirthView Answer on Stackoverflow
Solution 5 - RubyxavrileyView Answer on Stackoverflow
Solution 6 - RubyJustinView Answer on Stackoverflow
Solution 7 - RubyRobert SchaafView Answer on Stackoverflow