Quelle est la «bonne» façon de parcourir un tableau dans Ruby?

PHP, pour toutes ses verrues, est plutôt bon sur ce point. Il n’y a pas de différence entre un tableau et un hash (peut-être que je suis naïf, mais cela me semble évident) et pour parcourir soit vous faites soit

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

En Ruby, il y a plusieurs façons de faire ce genre de choses:

 array.length.times do |i| end array.each array.each_index for i in array 

Les hachages ont plus de sens, puisque je n’utilise que

 hash.each do |key, value| 

Pourquoi ne puis-je pas le faire pour les tableaux? Si je veux me souvenir d’une seule méthode, je suppose que je peux utiliser each_index (car cela rend l’index et la valeur disponibles), mais il est ennuyeux de devoir each_index array[index] au lieu d’une simple value .


Oh array.each_with_index , j’ai oublié array.each_with_index . Cependant, celui-ci est nul car il va |value, key| et hash.each va |key, value| ! N’est-ce pas fou?

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 sûr de votre question celle que vous recherchez.

Je pense qu’il n’y a pas un seul moyen. Il y a beaucoup de façons différentes d’itérer et chacune a sa propre niche.

  • each suffit pour de nombreux usages, 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 à “length.times”.
  • map est un autre moyen d’itérer, utile lorsque vous souhaitez transformer un tableau en un autre.
  • select est l’iterator à utiliser lorsque vous souhaitez choisir un sous-ensemble.
  • inject est utile pour générer des sums ou des produits ou pour collecter un seul résultat.

Cela peut sembler beaucoup à retenir, mais ne vous inquiétez pas, vous pouvez vous en sortir sans tout savoir. Mais au fur et à mesure que vous commencez à apprendre et à utiliser les différentes méthodes, votre code deviendra plus propre et plus clair et vous serez sur la voie de la maîsortingse Ruby.

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 une manière sensée de s’attendre à cet arrangement.

Lorsque je traite de tableaux, je me concentre sur les éléments du tableau (et non sur l’index car l’index est transitoire). La méthode est chacun avec un index, c.-à-d. Chaque + index, ou | chacun, index |, ou |value,index| . Ceci est également cohérent avec l’index vu comme un argument optionnel, par exemple | value | est équivalent à | value, index = nil | qui est compatible avec | value, index |.

Lorsque je traite 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 souhaitez que Duck-typing, soit explicitement utiliser une méthode définie comme Brent Longborough montré, soit une méthode implicite comme Maxhawkins l’a montré.

Ruby consiste à adapter le langage au programmeur, et non au programmeur adapté à la langue. C’est pourquoi il y a tellement de façons. Il y a tellement de façons de penser à quelque chose. Dans Ruby, vous choisissez le plus proche et le rest 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 “façon d’itérer dans un tableau dans Ruby?”, Bien, je pense que la manière principale (sans puissante puissance syntaxique ou orientée object) est de faire:

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

Mais Ruby est tout au sujet de puissantes puissances syntaxiques orientées sucre et object, mais de toute façon, voici l’équivalent des hachages, 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 suivante: “La bonne façon de parcourir un tableau dans Ruby dépend de vous (le programmeur ou l’équipe de programmation) et du projet.”. Le meilleur programmateur Ruby fait le meilleur choix (de quelle puissance syntaxique et / ou quelle approche orientée object). Le meilleur programmeur Ruby continue à chercher d’autres moyens.


Maintenant, je veux poser une autre question: “Quelle est la” bonne “façon de parcourir une plage en Ruby en arrière?”! (Cette question est comment je suis venu à cette page.)

C’est sympa à faire (pour les avants):

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

mais je n’aime pas faire (pour les arrière):

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

Eh bien, cela ne me dérange pas trop de le faire, mais quand j’enseigne en arrière, je veux montrer à mes étudiants une symésortinge agréable (avec une différence minimale, par exemple en ajoutant seulement un revers ou un pas -1, mais sans modifier autre chose). Vous pouvez faire (pour la symésortinge):

 (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 tu ne peux 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 le Ruby pur plutôt que des approches orientées object (pour l’instant). Je voudrais parcourir en arrière:

  • sans créer un tableau (pensez à 0..1000000000)
  • travailler pour n’importe quelle gamme (par exemple des chaînes, pas seulement des entiers)
  • sans utiliser de puissance supplémentaire orientée object (c’est-à-dire sans modification de classe)

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

(Merci pour votre temps en lisant ceci.)

Utilisez each_with_index lorsque vous avez besoin des deux.

 ary.each_with_index { |val, idx| # ... 

Les autres réponses sont correctes, mais je voulais souligner une autre chose périphérique: les tableaux sont ordonnés, alors que les hachages ne sont pas dans 1.8. (Dans Ruby 1.9, les hachages sont classés par ordre d’insertion des clés.) Il n’était donc pas judicieux, avant 1.9, d’itérer sur un hachage de la même manière / séquence que les tableaux, qui ont toujours été classés. Je ne sais pas quel est l’ordre par défaut pour les tableaux associatifs PHP (apparemment mon google fu n’est pas assez fort pour le comprendre), mais je ne sais pas comment vous pouvez considérer les tableaux PHP et les tableaux associatifs PHP classiques. être “les mêmes” dans ce contexte, puisque l’ordre des tableaux associatifs semble indéfini.

En tant que tel, la manière Ruby semble plus claire et intuitive pour moi. 🙂

Essayer de faire la même chose de manière cohérente avec les tableaux et les tables de hachage pourrait simplement être une odeur de code, mais si je suis considéré comme un demi-singe-patcher, si vous recherchez un comportement cohérent, cela vous aidera-t-il? ?:

 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}" } 

J’avais essayé de créer un menu (en camping et à Markaby ) en utilisant un hachage.

Chaque élément a 2 éléments: une étiquette de menu et une URL , donc un hachage semblait correct, mais l’URL ‘/’ pour ‘Home’ apparaissait toujours en dernier (comme on pouvait s’y attendre pour un hachage), donc les éléments de menu apparaissaient erronés commande.

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, un nom d’ identifiant CSS) signifie simplement augmenter la valeur de la tranche. Donc, comme un hachage mais avec des groupes composés d’un nombre quelconque d’éléments. Parfait.

Donc, c’est juste pour vous remercier d’avoir laissé entendre par inadvertance une solution!

Evident, mais cela vaut la peine de le préciser: je suggère de vérifier si la longueur du tableau est divisible par la valeur de la tranche.

Voici les quatre options répertorié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. Passez simplement par les valeurs:

     array.each 
  2. Passez simplement par les indices:

     array.each_index 
  3. Passer par index + variable index:

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

     array.length.times do | i | 

Si vous utilisez le mixin énumérable (comme le fait Rails), vous pouvez faire quelque chose de similaire à l’extrait de php répertorié. Il suffit d’utiliser la méthode each_slice et d’aplatir 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 patch de singe requirejs.

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 à la méthode d’aplatissement qui spécifie la profondeur de reconditionnement.

 # 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]] 

Quant à savoir si c’est une odeur de code, je ne suis pas sûr. Habituellement, lorsque je dois me pencher en arrière pour parcourir quelque chose, je me recule et je réalise que j’attaque mal le problème.

Le bon chemin est celui avec lequel vous vous sentez le plus à l’aise et qui fait ce que vous voulez qu’il fasse. Dans la programmation, il y a rarement une façon «correcte» de faire les choses, le plus souvent, il y a plusieurs façons de choisir.

Si vous êtes à l’aise avec certaines manières de faire les choses, faites-le simplement, à moins que cela ne fonctionne pas – alors il est temps de trouver un meilleur moyen.

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 --