Was ist der 'Ruby-Weg', um zwei Arrays gleichzeitig zu durchlaufen?


127

Eher eine Syntax-Neugier als ein zu lösendes Problem ...

Ich habe zwei Arrays gleicher Länge und möchte beide gleichzeitig durchlaufen - zum Beispiel, um beide Werte an einem bestimmten Index auszugeben.

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

Ich weiß, dass ich each_indexdie Arrays wie folgt verwenden und indizieren kann :

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

Gibt es einen Ruby-Weg , um dies besser zu machen? Etwas wie das?

# 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
Sind beide Arrays gleich groß?
Anurag

1
Ja - beide sind bekanntermaßen gleich lang
nfm

Antworten:


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

12
'.each' kann Array-Elemente entfalten? Ich frage mich, wie viel mehr von Ruby ich nicht weiß: /
Nikita Rybak

5
Wenn Sie einen solchen Ausdruck verwenden möchten, ist es immer am besten, Klammern für Methodenaufrufe zu verwenden, nur für den Fall. @ budget.zip (@actual) .each
AboutRuby

1
@AboutRuby: In diesem Fall wird es benötigt! Fest.
Marc-André Lafortune

2
Wenn ich solche Rubinlinien sehe, werfe ich meine Hände in die Luft und gehe wie ein Champion durch den Raum!
iGbanam

9
Ist diese Skala? Wenn ich 2 Arrays mit 10000 Elementen habe, muss dafür ein Array mit 20.000 Elementen erstellt werden? Die Dokumente schlagen dies vor.
Tom Andersen

21

Verwenden Sie die Array.zipMethode und übergeben Sie ihr einen Block, um die entsprechenden Elemente nacheinander zu durchlaufen.


20

Es gibt eine andere Möglichkeit, zwei Arrays gleichzeitig mithilfe von Enumeratoren zu durchlaufen:

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

Enumeratoren sind leistungsfähiger als die oben verwendeten Beispiele, da sie unter anderem unendliche Reihen und parallele Iterationen ermöglichen.


1
Gibt es eine Möglichkeit, den Index in den loopoben gezeigten zu bringen?
Biju

16

Zusätzlich zu dem, a.zip(b).each{|x,y| }was andere gesagt haben, kann man auch sagen [a,b].transpose.each{|x,y| }, was mir ein bisschen symmetrischer vorkommt. Wahrscheinlich nicht so schnell, da Sie das zusätzliche [a,b]Array erstellen .


11
+1 Eines der schönen Dinge transposeist, dass es eine Ausnahme auslöst, wenn die beiden Arrays nicht gleich lang sind.
Andrew Grimm

14

Bezogen auf die ursprüngliche Frage, können Sie Arrays mit ungleicher Länge durchlaufen, in denen die Werte durchlaufen sollen

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

und Ruby wird dir geben

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

Dies erspart Ihnen die nilWerte, die Sie nur durch die Verwendung von zip erhalten


6

Das einfache Zusammenzippen der beiden Arrays funktioniert gut, wenn Sie mit Arrays arbeiten. Aber was ist, wenn Sie es mit endlosen Enumeratoren wie diesen zu tun haben:

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

enum1.zip(enum2)schlägt fehl, weil zipversucht wird, alle Elemente auszuwerten und zu kombinieren. Tun Sie stattdessen Folgendes:

enum1.lazy.zip(enum2)

Dieser lazyspart Ihnen, indem er den resultierenden Enumerator faul auswertet.


2

Wie wäre es mit Kompromissen und der Verwendung von #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] }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.