可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
回答1:
>> @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]]
回答2:
Use the Array.zip
method and pass it a block to iterate over the corresponding elements sequentially.
回答3:
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.
回答4:
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.
回答5:
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
回答6:
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.
回答7:
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] }