Jaki jest „sposób Rubiego” na iterację dwóch tablic naraz

128

Bardziej ciekawa składnia niż problem do rozwiązania ...

Mam dwie tablice o równej długości i chcę wykonać iterację po obu naraz - na przykład wypisać obie ich wartości w określonym indeksie.

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

Wiem, że mogę używać each_indexi indeksować tablice w następujący sposób:

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

Czy istnieje sposób w Rubim, aby to zrobić lepiej? Coś takiego ?

# 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
nfm
źródło
1
czy obie tablice mają równe rozmiary?
Anurag
1
Tak - wiadomo, że oba mają tę samą długość
nfm

Odpowiedzi:

278
>> @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]]
John La Rooy
źródło
12
„.each” może rozwinąć elementy tablicy? Zastanawiam się, ile jeszcze Rubiego nie wiem: /
Nikita Rybak
5
Jeśli zamierzasz użyć takiego wyrażenia, zawsze najlepiej jest używać nawiasów do wywołań metod, na wszelki wypadek. @ budget.zip (@actual)
.each
1
@AboutRuby: W tym przypadku jest to potrzebne! Naprawiony.
Marc-André Lafortune
2
kiedy widzę takie rubinowe linie, wyrzucam ręce w powietrze i chodzę po pokoju jak mistrz!
iGbanam,
9
Czy to skala? Jeśli mam 2 tablice 10000 pozycji, czy wymaga to utworzenia tablicy 20 000 pozycji? Doktorzy sugerują to.
Tom Andersen
21

Użyj tej Array.zipmetody i przekaż jej blok, aby kolejno iterować po odpowiednich elementach.

Anurag
źródło
20

Istnieje inny sposób iteracji po dwóch tablicach naraz przy użyciu modułów wyliczających:

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

Enumeratory są potężniejsze niż przykłady użyte powyżej, ponieważ pozwalają między innymi na nieskończone serie i równoległe iteracje.

Donato
źródło
1
Czy istnieje sposób, aby uzyskać indeks wewnątrz looppokazanego powyżej?
Biju,
16

Oprócz tego, a.zip(b).each{|x,y| }jak powiedzieli inni, możesz również powiedzieć [a,b].transpose.each{|x,y| }, co wydaje mi się nieco bardziej symetryczne. Prawdopodobnie nie tak szybko, ponieważ tworzysz dodatkową [a,b]tablicę.

Paul A Jungwirth
źródło
11
+1 Jedną z fajnych rzeczy transposejest to, że wywołuje wyjątek, jeśli dwie tablice nie są tej samej długości.
Andrew Grimm,
14

W odniesieniu do pierwotnego pytania, do iteracji po tablicach o nierównej długości, w których chcesz, aby wartości były cykliczne, możesz użyć

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

a Ruby ci to da

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

Oszczędza to nilwartości, które uzyskasz po samym użyciu zip

xavriley
źródło
6

Po prostu spakowanie dwóch tablic razem działa dobrze, jeśli masz do czynienia z tablicami. Ale co, jeśli masz do czynienia z niekończącymi się wyliczającymi, takimi jak coś takiego:

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

enum1.zip(enum2)zawodzi, ponieważ zippróbuje ocenić wszystkie elementy i połączyć je. Zamiast tego zrób to:

enum1.lazy.zip(enum2)

To lazyoszczędza, sprawiając, że wynikowy moduł wyliczający jest leniwy.

Justin
źródło
2

A co z kompromitacją i używaniem #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] }
Robert Schaaf
źródło