Quelle est la `` manière Ruby '' d'itérer sur deux tableaux à la fois

127

Plus une curiosité syntaxique qu'un problème à résoudre ...

J'ai deux tableaux de longueur égale et je veux les parcourir tous les deux à la fois - par exemple, pour afficher leurs deux valeurs à un certain index.

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

Je sais que je peux utiliser each_indexet indexer dans les tableaux comme ceci:

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

Existe-t-il un moyen Ruby de mieux faire cela? Quelque chose comme ça?

# 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
la source
1
les deux tableaux sont-ils de tailles égales?
Anurag
1
Oui - tous deux connus pour être de la même longueur
nfm

Réponses:

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]]
John La Rooy
la source
12
«.each» peut déplier des éléments de tableau? Je me demande combien plus de Ruby je ne sais pas: /
Nikita Rybak
5
Si vous envisagez d'utiliser une expression comme celle-là, il est toujours préférable d'utiliser des parenthèses pour les appels de méthode, juste au cas où. @ budget.zip (@actual) .each
AboutRuby
1
@AboutRuby: Dans ce cas, c'est nécessaire! Fixé.
Marc-André Lafortune
2
quand je vois des lignes de rubis comme ça, je jette mes mains en l'air et je fais le tour de la pièce comme un champion!
iGbanam
9
Est-ce que cette échelle? Si j'ai 2 tableaux de 10000 éléments, cela nécessite-t-il de créer un tableau de 20 000 éléments? Les documents le suggèrent.
Tom Andersen
21

Utilisez le Array.zip méthode et passez-lui un bloc pour parcourir les éléments correspondants de manière séquentielle.

Anurag
la source
20

Il existe une autre façon d'itérer sur deux tableaux à la fois en utilisant des énumérateurs:

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

Les énumérateurs sont plus puissants que les exemples utilisés ci-dessus, car ils permettent des séries infinies, des itérations parallèles, entre autres techniques.

Donato
la source
1
Existe-t-il un moyen d'obtenir l'index à l'intérieur de l' loopillustration ci-dessus?
Biju
16

En plus de ce a.zip(b).each{|x,y| }que d'autres ont dit, vous pouvez également dire [a,b].transpose.each{|x,y| }, ce qui me semble un peu plus symétrique. Probablement pas aussi rapide, cependant, puisque vous créez le [a,b]tableau supplémentaire .

Paul A Jungwirth
la source
11
+1 Une des choses intéressantes transposeest qu'il lève une exception si les deux tableaux ne sont pas de la même longueur.
Andrew Grimm
14

Lié à la question d'origine, pour itérer sur des tableaux de longueur inégale où vous voulez que les valeurs circulent, vous pouvez utiliser

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

et Ruby vous donnera

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

Cela vous évite les nilvaleurs que vous obtiendrez en utilisant simplement zip

Xavriley
la source
6

Le simple fait de compresser les deux tableaux ensemble fonctionne bien si vous avez affaire à des tableaux. Mais que se passe-t-il si vous avez affaire à des énumérateurs sans fin, tels que quelque chose comme ceux-ci:

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

enum1.zip(enum2)échoue car zipessaie d'évaluer tous les éléments et de les combiner. Faites plutôt ceci:

enum1.lazy.zip(enum2)

Celui-ci lazyvous sauve en faisant l'évaluation paresseuse de l'énumérateur résultant.

Justin
la source
2

Que diriez-vous de faire des compromis et d'utiliser #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
la source