web-dev-qa-db-fra.com

Quelle est la "bonne" méthode pour parcourir un tableau dans Ruby?

PHP, pour toutes ses verrues, est très bon sur ce point. Il n’ya pas de différence entre un tableau et un hachage (peut-être que je suis naïf, mais cela me semble tout à fait juste) et, si vous le faites, parcourez-le

foreach (array/hash as $key => $value)

Dans Ruby il y a un tas de façons de faire ce genre de chose:

array.length.times do |i|
end

array.each

array.each_index

for i in array

Les hachages ont plus de sens, car j’utilise toujours

hash.each do |key, value|

Pourquoi est-ce que je ne peux pas faire cela pour les tableaux? Si je veux me souvenir d'une seule méthode, je suppose que je peux utiliser each_index (car cela donne à la fois l'index et la valeur disponible), mais il est agaçant de devoir faire array[index] au lieu de value.


Oh oui, j'ai oublié array.each_with_index. Cependant, celui-ci craint parce qu'il va |value, key| et hash.each va |key, value|! N'est-ce pas fou?

323
Tom Lehman

Cela va parcourir tous les éléments:

array = [1, 2, 3, 4, 5, 6]
array.each { |x| puts x }

Impressions:

1
2
3
4
5
6

Cela va parcourir tous les éléments en vous donnant la valeur et l'index:

array = ["A", "B", "C"]
array.each_with_index {|val, index| puts "#{val} => #{index}" }

Impressions:

A => 0
B => 1
C => 2

Je ne suis pas tout à fait sûr de votre question de savoir laquelle vous cherchez.

536
Robert Gamble

Je pense qu'il n'y a personne à droite manière. Il y a beaucoup de façons différentes d'itérer, et chacune a son propre créneau.

  • each est suffisant pour de nombreuses utilisations, car je ne me soucie pas souvent des index.
  • each_ with _index agit comme Hash # chacun - vous obtenez la valeur et l'index.
  • each_index - juste les index. Je ne l'utilise pas souvent. Équivalent à "longueur.times".
  • map est un autre moyen d'itérer, utile lorsque vous souhaitez transformer un tableau en un autre.
  • select est l'itérateur à utiliser pour choisir un sous-ensemble.
  • inject est utile pour générer des sommes ou des produits, ou pour collecter un seul résultat.

Il peut sembler beaucoup de choses à retenir, mais ne vous inquiétez pas, vous pouvez vous en tirer sans les connaître tous. Mais au fur et à mesure que vous commencerez à apprendre et à utiliser les différentes méthodes, votre code deviendra plus propre et plus clair, et vous serez sur le chemin de la maîtrise de Ruby.

80
AShelly

Je ne dis pas que Array -> |value,index| et Hash -> |key,value| n'est pas fou (voir le commentaire de Horace Loeb), mais je dis qu'il y a un sens moyen d'attendre cet arrangement.

Lorsque je traite de tableaux, je me concentre sur les éléments du tableau (et non sur l'index car il est transitoire). La méthode consiste à utiliser chacun un index, c’est-à-dire chaque + index ou | chaque index | ou |value,index|. Cela correspond également au fait que l’index soit considéré comme un argument facultatif, par exemple. | valeur | est équivalent à | valeur, index = nil | ce qui est cohérent avec | valeur, index |.

Lorsque je traite avec des hachages, je suis souvent plus concentré sur les clés que sur les valeurs, et je traite généralement les clés et les valeurs dans cet ordre, soit key => value ou hash[key] = value.

Si vous voulez utiliser le type de canard, utilisez soit explicitement une méthode définie, comme le montre Brent Longborough, soit implicite, comme le montre maxhawkins.

Ruby consiste à adapter la langue au programmeur, pas au programmeur à l'adapter à la langue. C'est pourquoi il y a tant de façons. Il y a tellement de façons de penser à quelque chose. Dans Ruby, vous choisissez le plus proche et le reste du code tombe généralement de manière extrêmement nette et concise.

En ce qui concerne la question initiale, "Quelle est la" bonne "méthode pour parcourir un tableau en Ruby?", Eh bien, je pense que la méthode principale (c'est-à-dire sans sucre syntaxique puissant ou pouvoir orienté objet) consiste à:

for index in 0 ... array.size
  puts "array[#{index}] = #{array[index].inspect}"
end

Mais Ruby concerne le sucre syntaxique puissant et le pouvoir orienté objet, mais voici l’équivalent pour le hachage, et les clés peuvent être commandées ou non:

for key in hash.keys.sort
  puts "hash[#{key.inspect}] = #{hash[key].inspect}"
end

Donc, ma réponse est: "La" bonne "façon de parcourir un tableau dans Ruby dépend de vous (c'est-à-dire du programmeur ou de l'équipe de programmation) et du projet.". Le meilleur programmeur Ruby fait le meilleur choix (quelle puissance syntaxique et/ou quelle approche orientée objet). Le meilleur programmeur Ruby continue de rechercher plus de moyens.


Maintenant, je veux poser une autre question, "Quelle est la" bonne "façon de parcourir une plage dans Ruby à l'envers?"! (Cette question est comment je suis arrivé à cette page.)

C'est agréable à faire (pour les avants):

(1..10).each{|i| puts "i=#{i}" }

mais je n'aime pas faire (pour les arrières):

(1..10).to_a.reverse.each{|i| puts "i=#{i}" }

Eh bien, cela ne me dérange pas vraiment de faire trop, mais quand j'enseigne le recul, je veux montrer à mes étudiants une symétrie de Nice (c'est-à-dire avec une différence minimale, par exemple en ajoutant seulement un inverse ou un pas -1, mais sans modifier quoi que ce soit d'autre). Vous pouvez faire (pour la symétrie):

(a=*1..10).each{|i| puts "i=#{i}" }

et

(a=*1..10).reverse.each{|i| puts "i=#{i}" }

ce que je n'aime pas beaucoup, mais vous ne pouvez pas faire

(*1..10).each{|i| puts "i=#{i}" }
(*1..10).reverse.each{|i| puts "i=#{i}" }
#
(1..10).step(1){|i| puts "i=#{i}" }
(1..10).step(-1){|i| puts "i=#{i}" }
#
(1..10).each{|i| puts "i=#{i}" }
(10..1).each{|i| puts "i=#{i}" }   # I don't want this though.  It's dangerous

Vous pourriez finalement faire

class Range

  def each_reverse(&block)
    self.to_a.reverse.each(&block)
  end

end

mais je veux enseigner pure Ruby plutôt que sur les approches orientées objet (pour l'instant). Je voudrais revenir en arrière:

  • sans créer de tableau (considérez 0..1000000000)
  • travaillant pour n’importe quelle plage (par exemple, des chaînes, pas seulement des entiers)
  • sans utiliser de puissance supplémentaire orientée objet (c'est-à-dire sans modification de classe)

Je pense que cela est impossible sans définir une méthode pred, ce qui signifie modifier la classe Range pour l’utiliser. Si vous pouvez le faire, s'il vous plaît, faites-le moi savoir, sinon une confirmation d'impossibilité serait appréciée, bien que ce soit décevant. Peut-être que Ruby 1.9 répond à cela.

(Merci pour votre temps en lisant ceci.)

59
Luis Esteban

Utilisez each_with_index lorsque vous avez besoin des deux.

ary.each_with_index { |val, idx| # ...
17
J Cooper

Les autres réponses sont très bien, mais je voulais souligner une autre chose périphérique: les tableaux sont ordonnés, alors que les hachages ne sont pas en 1.8. (Dans Ruby 1.9, les hachages sont ordonnés en fonction de l'ordre d'insertion des clés.) Il serait donc inconcevable, avant la version 1.9, de parcourir un hachage de la même manière/séquence que les tableaux, qui ont toujours eu une commande définitive. Je ne sais pas quel est l'ordre par défaut pour PHP les tableaux associatifs (apparemment, mon fu google n'est pas assez puissant pour le comprendre, non plus), mais je ne sais pas comment vous pouvez considérer un _ normal. Les tableauxPHP et les tableaux associatifs PHP doivent être "identiques" dans ce contexte, car l'ordre des tableaux associatifs semble indéfini.

En tant que tel, le chemin Ruby me semble plus clair et intuitif. :)

12
Pistos

Essayer de faire la même chose avec les tableaux et les hachages peut-être juste une odeur de code, mais, au risque de me faire passer pour un codeur mi-singe codor, si vous recherchez une cohérence comportement, est-ce que cela ferait l'affaire ?:

class Hash
    def each_pairwise
        self.each { | x, y |
            yield [x, y]
        }
    end
end

class Array
    def each_pairwise
        self.each_with_index { | x, y |
            yield [y, x]
        }
    end
end

["a","b","c"].each_pairwise { |x,y|
    puts "#{x} => #{y}"
}

{"a" => "Aardvark","b" => "Bogle","c" => "Catastrophe"}.each_pairwise { |x,y|
    puts "#{x} => #{y}"
}
7
Brent.Longborough

Voici les quatre options énumérées dans votre question, classées par liberté de contrôle. Vous voudrez peut-être en utiliser un autre en fonction de vos besoins.

  1. Il suffit de passer par les valeurs:

    array.each
    
  2. Il suffit de parcourir les index:

    array.each_index
    
  3. Passer par indices + variable d'index:

    for i in array
    
  4. Nombre de boucles de contrôle + variable d'index:

    array.length.times do | i |
    
6
Jake

J'avais essayé de construire un menu (dans Camping et Markaby ) en utilisant un hash.

Chaque élément comporte 2 éléments: a étiquette de men et a URL, un hachage semblait donc correct, mais l'URL '/' de 'Home' est toujours apparue en dernier (comme vous attendez pour un hachage), les éléments de menu sont donc apparus dans le mauvais ordre.

Utiliser un tableau avec each_slice fait le travail:

['Home', '/', 'Page two', 'two', 'Test', 'test'].each_slice(2) do|label,link|
   li {a label, :href => link}
end

Ajouter des valeurs supplémentaires pour chaque élément de menu (par exemple, comme un nom CSS ID) signifie simplement augmenter la valeur de la tranche. Donc, comme un hachage mais avec des groupes composés d’un nombre quelconque d’articles. Parfait.

Donc, ceci est juste pour dire merci pour avoir laissé entendre par inadvertance une solution!

Évident, mais utile de préciser: je suggère de vérifier si la longueur du tableau est divisible par la valeur de la tranche.

5
Dave Everitt

Si vous utilisez le enumerable mixin (comme le fait Rails), vous pouvez faire quelque chose de similaire à l'extrait de code php répertorié. Utilisez simplement la méthode each_slice et aplatissez le hachage.

require 'enumerator' 

['a',1,'b',2].to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }

# is equivalent to...

{'a'=>1,'b'=>2}.to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }

Moins de corrections de singe requises.

Toutefois, cela pose des problèmes lorsque vous avez un tableau récursif ou un hachage avec des valeurs de tableau. Dans Ruby 1.9, ce problème est résolu avec un paramètre correspondant à la méthode flatten qui spécifie la profondeur de recurse.

# Ruby 1.8
[1,2,[1,2,3]].flatten
=> [1,2,1,2,3]

# Ruby 1.9
[1,2,[1,2,3]].flatten(0)
=> [1,2,[1,2,3]]

En ce qui concerne la question de savoir s'il s'agit d'une odeur de code, je ne suis pas sûr. Habituellement, lorsque je dois me mettre en quatre pour parcourir quelque chose, je me retire et réalise que je m'attaque mal au problème.

3
maxhawkins

La bonne manière est celle avec laquelle vous vous sentez le plus à l'aise et qui fait ce que vous voulez. En programmation, il y a rarement une manière "correcte" de faire les choses, mais le plus souvent, il y a plusieurs façons de choisir.

Si vous êtes à l'aise avec certaines façons de faire, faites-le, à moins que cela ne fonctionne pas - alors il est temps de trouver une meilleure façon.

2

Dans Ruby 2.1, la méthode each_with_index est supprimée. Au lieu de cela, vous pouvez utiliser each_index

Exemple:

a = [ "a", "b", "c" ]
a.each_index {|x| print x, " -- " }

produit:

0 -- 1 -- 2 --
2
Amjed Shareef