¿Cuál es la 'forma Ruby' para iterar sobre dos matrices a la vez?


127

Más una curiosidad sintáctica que un problema a resolver ...

Tengo dos matrices de igual longitud, y quiero iterar sobre ambas a la vez, por ejemplo, para emitir ambos valores en un cierto índice.

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

Sé que puedo usar each_indexe indexar en las matrices de esta manera:

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

¿Hay alguna manera de Ruby para hacer esto mejor? Algo como esto?

# 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
son ambas las matrices de igual tamaño?
— Anurag

1
Sí, ambos se sabe que son de la misma longitud
— nfm

Respuestas:


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' puede desplegar elementos de matriz? Me pregunto cuánto más de Ruby no sé: /
— Nikita Rybak

55
Si va a usar una expresión como esa, siempre es mejor usar paréntesis para las llamadas a métodos, por si acaso. @ budget.zip (@actual) .each
— AboutRuby

1
@AboutRuby: ¡En este caso, es necesario! Fijo.
— Marc-André Lafortune

2
Cuando veo líneas de rubí como esta, lanzo mis manos al aire y doy vueltas por la habitación como un campeón.
— iGbanam

9
¿Esto escala? Si tengo 2 matrices de 10000 artículos, ¿esto requiere hacer una matriz de 20,000 artículos? Los documentos sugieren esto.
— Tom Andersen

21

Use el Array.zipmétodo y páselo un bloque para iterar sobre los elementos correspondientes secuencialmente.


20

Hay otra forma de iterar sobre dos matrices a la vez utilizando enumeradores:

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

Los enumeradores son más potentes que los ejemplos utilizados anteriormente, ya que permiten series infinitas, iteraciones paralelas, entre otras técnicas.


1
¿Hay alguna manera de obtener el índice dentro de lo que se loopmuestra arriba?
— Biju

16

Además de lo a.zip(b).each{|x,y| }que otros han dicho, también se puede decir [a,b].transpose.each{|x,y| }, lo que me parece un poco más simétrico. Sin embargo, probablemente no sea tan rápido, ya que está creando la [a,b]matriz adicional .


11
+1 Una de las cosas buenas de esto transposees que genera una excepción si las dos matrices no tienen la misma longitud.
— Andrew Grimm

14

Relacionado con la pregunta original, para iterar sobre matrices de longitud desigual donde desea que los valores circulen, puede usar

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

y Ruby te dará

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

Esto te salva de los nilvalores que obtendrás con solo usar zip


6

Simplemente comprimir las dos matrices juntas funciona bien si se trata de matrices. Pero, ¿qué pasa si se trata de enumeradores interminables, como algo como esto:

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

enum1.zip(enum2)falla porque zipintenta evaluar todos los elementos y combinarlos. En cambio, haz esto:

enum1.lazy.zip(enum2)

Ese lazyte salva al hacer que el enumerador resultante sea de evaluación diferida.


2

¿Qué tal comprometerse y usar #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] }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.