Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

People also ask

How do you iterate through an array in Ruby?

The Ruby Enumerable#each method is the most simplistic and popular way to iterate individual items in an array. It accepts two arguments: the first being an enumerable list, and the second being a block. It takes each element in the provided list and executes the block, taking the current item as a parameter.

How do you add two arrays in Ruby?

This can be done in a few ways in Ruby. The first is the plus operator. This will append one array to the end of another, creating a third array with the elements of both. Alternatively, use the concat method (the + operator and concat method are functionally equivalent).

What does the .each method do in Ruby?

The each{} method allows one loop through the elements of an array to perform operations on them. It is an enumerator function that allows you to iterate over elements of an array and returns an array.


>> @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]]

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


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.


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.


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


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.