“Ruby方式”同时迭代两个数组的方法是什么?

138

这更像是一种语法上的好奇心,而不是需要解决的问题...

我有两个长度相等的数组,想要同时迭代它们 - 例如,在特定索引处输出它们的值。

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

我知道可以使用each_index并像下面这样索引数组:

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

有没有更好的 Ruby方法 来做这件事?类似于这样的东西?

# 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
这两个数组的大小是否相等? - Anurag
1
是的 - 两者被认为具有相同的长度。 - nfm
7个回答

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

13
'.each' 可以展开数组元素吗?我想知道还有多少 Ruby 的知识我不会 :/ - Nikita Rybak
5
如果你要使用这样的表达方式,最好为方法调用使用括号,以防万一。@budget.zip(@actual).each - AboutRuby
2
当我看到像这样的 Ruby 代码时,我会高举双手,在房间里走来走去,就像一位冠军! - Igbanam
1
我知道你的例子反映了问题,但我认为你应该改变你的例子,使其中一个是数字,另一个是字母。这将使压缩数组中正在发生的事情以及哪些元素去了哪里更加清晰明了。 - xaxxon
10
这是否可扩展?如果我有两个包含1万项的数组,是否需要创建一个包含2万项的数组?文档似乎是这样建议的。 - Tom Andersen
显示剩余6条评论

21

使用枚举器还有另一种同时迭代两个数组的方法:

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

枚举器比上面使用的示例更强大,因为它们允许无限序列、并行迭代等其他技术。


1
上面的loop中有没有一种方法可以获取索引? - Biju
@Biju,你可以编写 e1 = [1,2,4].each_with_indexe2 = [5,6,7].each_with_index。然后 e1.next #=> [1, 0]; e1.next #=> [2, 1]; e1.next #=> [4, 2]; e1.next #=> StopIteration: iteration reached an end (exception)e2 的操作类似。 - Cary Swoveland

21

使用 Array.zip 方法并传入一个块来顺序迭代相应的元素。


17

与原问题相关,如果要遍历长度不相等的数组,并希望值循环使用,可以使用以下方法:

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)

而Ruby将会给你

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

使用这样的方法可以避免仅使用zip产生的nil值。


16

除了其他人提到的a.zip(b).each{|x,y| }外,您还可以使用[a,b].transpose.each{|x,y| },这让我感觉稍微对称一点。不过可能不够快,因为你需要创建额外的[a,b]数组。


11
transpose的一个好处是,如果两个数组的长度不同,它会引发一个异常。 - Andrew Grimm

7

如果你处理的是数组,那么将两个数组简单地压缩在一起就很有效。但是,如果你要处理无止境的枚举器,例如下面这些内容,该怎么办:

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

enum1.zip(enum2) 失败是因为 zip 尝试评估并组合所有元素。相反,使用以下方法:

enum1.lazy.zip(enum2)

那个“懒惰的”(lazy)可以通过使得生成的枚举器进行惰性求值来节省您的时间。

如果您想使用数组但希望保持内存占用低,这也非常有用。 - ggorlen

3
如何妥协,使用#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] }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接