Qual è il "modo di Ruby" per scorrere due array contemporaneamente


127

Più una curiosità di sintassi che un problema da risolvere ...

Ho due matrici di uguale lunghezza e voglio scorrere contemporaneamente su entrambi - ad esempio, per generare entrambi i loro valori in un determinato indice.

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

So che posso usare each_indexe indicizzare le matrici in questo modo:

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

Esiste un modo Ruby per farlo meglio? Qualcosa del genere ?

# 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
sono entrambi array di uguali dimensioni?
Anurag,

1
Sì - entrambi noti per essere della stessa lunghezza
nfm

Risposte:


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' può spiegare gli elementi dell'array? Mi chiedo quanto altro di Ruby non conosco: /
Nikita Rybak il

5
Se hai intenzione di usare un'espressione del genere, è sempre meglio usare le parentesi per le chiamate al metodo, per ogni evenienza. @ budget.zip (@actual) .each
AboutRuby

1
@AboutRuby: in questo caso, è necessario! Fisso.
Marc-André Lafortune,

2
quando vedo linee di rubino come questa, lancio le mani in aria e giro la stanza come un campione!
iGbanam,

9
Questa scala? Se ho 2 10000 array di articoli, è necessario creare un array di 20.000 articoli? I documenti suggeriscono questo.
Tom Andersen il

21

Utilizzare il Array.zipmetodo e passargli un blocco per iterare in sequenza sugli elementi corrispondenti.


20

Esiste un altro modo per scorrere due array contemporaneamente utilizzando gli enumeratori:

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

Gli enumeratori sono più potenti degli esempi usati in precedenza, perché consentono serie infinite, iterazioni parallele, tra le altre tecniche.


1
C'è un modo per ottenere l'indice all'interno di quello loopmostrato sopra?
Biju,

16

Oltre a a.zip(b).each{|x,y| }come altri hanno già detto, puoi anche dire [a,b].transpose.each{|x,y| }, il che mi sembra un po 'più simmetrico. Probabilmente non è così veloce, dal momento che stai creando l' [a,b]array extra .


11
+1 Una delle cose belle transposeè che genera un'eccezione se i due array non hanno la stessa lunghezza.
Andrew Grimm,

14

Relativo alla domanda originale, per iterare su matrici di lunghezza disuguale in cui si desidera che i valori scorrano ciclicamente, è possibile utilizzare

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

e Ruby ti darà

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

Questo ti salva dai nilvalori che otterrai semplicemente usando zip


6

La semplice compressione delle due matrici insieme funziona bene se si ha a che fare con le matrici. Ma cosa succede se hai a che fare con enumeratori infiniti, come qualcosa del genere:

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

enum1.zip(enum2)fallisce perché zipcerca di valutare tutti gli elementi e combinarli. Invece, fai questo:

enum1.lazy.zip(enum2)

Quello lazyti salva rendendo pigro-valutare l'enumeratore risultante.


2

Che ne dici di scendere a compromessi e di usare #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] }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.