2010-08-26 6 views
105

Más de una curiosidad sintaxis de un problema a resolver ...¿Cuál es el "camino de Ruby para iterar sobre dos matrices al tiempo

Tengo dos matrices de igual longitud, y quiere iterar sobre los dos a la vez - por ejemplo, para dar salida a ambos valores en un índice determinado.

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

sé que puedo usar each_index e índice en las matrices de este modo:

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

¿Hay una manera de Ruby 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

¿Las dos matrices son de igual tamaño? – Anurag

+1

Sí, ambos conocidos por tener la misma longitud – nfm

Respuesta

241
>> @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é:/ –

+1

¡Me encanta Ruby!:) – nfm

+5

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

21

utiliza el método Array.zip y pasarlo un bloque para iterar sobre los elementos correspondientes secuencialmente.

+0

perfecto, fácil y eficiente – akostadinov

16

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

+11

+1 Una de las cosas buenas de 'transpose' es que plantea una excepción si las dos matrices no tienen la misma longitud. –

18

hay otra manera para 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 

Enumeradores son más poderosos que los ejemplos utilizados anteriormente, debido a que permiten serie infinita, iteración paralelo, entre otras técnicas.

13

relacionada a la pregunta original, para iterar sobre matrices de longitud desigual en la que desea los valores del ciclo alrededor, se puede utilizar

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

y Ruby le dará

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

Este lo salva de los valores nil que obtendrá al usar el código postal

+1

Bonito mensaje de Ruby. – alistairholt

+1

esto es lo que estaba buscando, ¡gracias! – Malachi

5

Simplemente comprimir los dos las matrices juntas funcionan bien si se trata de matrices. Pero lo que si se trata de enumeradores sin fin, como algo así como estas:

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

enum1.zip(enum2) no es posible porque zip trata de evaluar todos los elementos y combinarlos. En su lugar, hacer esto:

enum1.lazy.zip(enum2) 

Que uno lazy le ahorra al hacer que el empadronador que resulta vago a evaluar.

1

¿Qué tal comprometer 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] } 
Cuestiones relacionadas