Wat is de “juiste” manier om een array in Ruby te doorlopen?

PHP, ondanks al zijn wratten, is op dit punt redelijk goed. Er is geen verschil tussen een array en een hash (misschien ben ik naïef, maar dit lijkt me duidelijk juist), en om ofwel te herhalen, doe je dat gewoon

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

In Ruby zijn er een heleboel manieren om dit soort dingen te doen:

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

Hashes zijn logischer, omdat ik gewoon altijd gebruik

hash.each do |key, value|

Waarom kan ik dit niet doen voor arrays? Als ik slechts één methode wil onthouden, denk ik dat ik each_indexkan gebruiken (omdat het zowel de index als de waarde beschikbaar maakt), maar het is vervelend om array[index]in plaats van alleen value.


O ja, ik was array.each_with_indexvergeten. Deze is echter waardeloos omdat het |value, key|en hash.eachgaat |key, value|! Is dit niet krankzinnig?


Antwoord 1, autoriteit 100%

Dit herhaalt alle elementen:

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

Afdrukken:

1
2
3
4
5
6

Dit doorloopt alle elementen en geeft je de waarde en de index:

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

Afdrukken:

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

Ik weet op basis van uw vraag niet helemaal zeker welke u zoekt.


Antwoord 2, autoriteit 17%

Ik denk dat er niet één juistemanier is. Er zijn veel verschillende manieren om te herhalen, en elk heeft zijn eigen niche.

  • eachis voldoende voor veel gebruik, aangezien ik niet vaak om de indexen geef.
  • each_ with _indexgedraagt zich als Hash#each – je krijgt de waarde en de index.
  • each_index– alleen de indexen. Ik gebruik deze niet vaak. Gelijk aan “length.times”.
  • mapis een andere manier om te herhalen, handig als je de ene array in een andere wilt transformeren.
  • selectis de iterator die je moet gebruiken als je een subset wilt kiezen.
  • injectis handig voor het genereren van sommen of producten, of het verzamelen van een enkel resultaat.

Het lijkt misschien veel om te onthouden, maar maak je geen zorgen, je kunt het redden zonder ze allemaal te kennen. Maar naarmate je de verschillende methoden begint te leren en gebruiken, wordt je code schoner en duidelijker en ben je op weg naar Ruby-beheersing.


Antwoord 3, autoriteit 10%

Ik zeg niet dat Array-> |value,index|en Hash-> |key,value|is niet krankzinnig (zie de opmerking van Horace Loeb), maar ik zeg dat er een verstandige manier is om deze regeling te verwachten.

Als ik met arrays te maken heb, ben ik gefocust op de elementen in de array (niet de index omdat de index van voorbijgaande aard is). De methode is elk met index, d.w.z. each+index, of |each,index|, of |value,index|. Dit komt ook overeen met het feit dat de index als een optioneel argument wordt beschouwd, b.v. |waarde| is gelijk aan |value,index=nil| wat consistent is met |value,index|.

Als ik met hashes te maken heb, ben ik vaak meer gefocust op de sleutels dan op de waarden, en meestal heb ik te maken met sleutels en waarden in die volgorde, ofwel key => valueof hash[key] = value.

Als je eend wilt typen, gebruik dan expliciet een gedefinieerde methode zoals Brent Longborough liet zien, of een impliciete methode zoals maxhawkins liet zien.

Bij Ruby draait alles om het aanpassen van de taal aan de programmeur, niet over het aanpassen van de programmeur aan de taal. Daarom zijn er zoveel manieren. Er zijn zoveel manieren om over iets na te denken. In Ruby kies je de dichtstbijzijnde en de rest van de code valt er meestal heel netjes en beknopt uit.

Wat betreft de oorspronkelijke vraag, “Wat is de “juiste” manier om door een array in Ruby te itereren?”, wel, ik denk dat de belangrijkste manier (dwz zonder krachtige syntactische suiker of objectgeoriënteerde kracht) is om te doen:

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

Maar bij Ruby draait alles om krachtige syntactische suiker en objectgeoriënteerde kracht, maar hoe dan ook, hier is het equivalent voor hashes, en de sleutels kunnen worden besteld of niet:

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

Dus, mijn antwoord is: “De” juiste “weg naar het herhalen door een array in Ruby is afhankelijk van u (d.w.z. de programmeur of het programmeerteam) en het project.”. De betere Ruby-programmeur maakt de betere keuze (waarvan syntactische vermogen en / of welke objectgeoriënteerde aanpak). De betere Ruby-programmeur blijft op zoek naar meer manieren.


Nu, ik wil een andere vraag stellen, “wat is de” juiste “manier om te herhalen door een bereik in Ruby achteruit?”! (Deze vraag is hoe ik naar deze pagina ben gekomen.)

Het is leuk om te doen (voor de forwards):

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

Maar ik hou niet van doen (voor de achteruit):

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

Nou, ik vind het niet erg om dat te veel te doen, maar wanneer ik achteruit ga, wil ik mijn studenten een leuke symmetrie laten zien (dwz met minimaal verschil, bijvoorbeeld alleen een omgekeerde of een stap -1 toevoegen , maar zonder iets anders te wijzigen).
U kunt doen (voor symmetrie):

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

en

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

die ik niet leuk vind, maar je kunt niet doen

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

U kunt uiteindelijk

doen

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

maar ik wil pure Ruby leren in plaats van objectgeoriënteerde benaderingen (nu nog). Ik wil graag achteruit herhalen:

  • zonder een array te maken (denk aan 0..1000000000)
  • werkt voor elk bereik (bijv. Strings, niet alleen gehele getallen)
  • zonder gebruik te maken van extra objectgeoriënteerde kracht (d.w.z. geen klassewijziging)

Ik geloof dat dit onmogelijk is zonder een pred-methode te definiëren, wat betekent dat de Range-klasse moet worden aangepast om deze te gebruiken. Als je dit kunt doen, laat het me dan weten, anders zou bevestiging van onmogelijkheid op prijs worden gesteld, hoewel het teleurstellend zou zijn. Misschien lost Ruby 1.9 dit op.

(Bedankt voor uw tijd om dit te lezen.)


Antwoord 4, autoriteit 3%

Gebruik each_with_index wanneer je beide nodig hebt.

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

Antwoord 5, autoriteit 2%

De andere antwoorden zijn prima, maar ik wilde nog op een ander randgeval wijzen: arrays zijn geordend, terwijl hashes niet in 1.8 zitten. (In Ruby 1.9 worden hashes geordend op volgorde van invoeging van sleutels.) Het zou dus niet logisch zijn om vóór 1.9 een hash te herhalen op dezelfde manier/volgorde als arrays, die altijd een duidelijke volgorde hebben gehad. Ik weet niet wat de standaardvolgorde is voor PHP-associatieve arrays (blijkbaar is mijn google fu ook niet sterk genoeg om dat uit te zoeken), maar ik weet niet hoe je normale PHP-arrays en PHP-associatieve arrays kunt overwegen om “hetzelfde” zijn in deze context, aangezien de volgorde voor associatieve arrays ongedefinieerd lijkt.

Als zodanig lijkt de Ruby-manier mij duidelijker en intuïtiever. 🙂


Antwoord 6, autoriteit 2%

Hier zijn de vier opties in uw vraag, gerangschikt door vrijheid van controle. Misschien wilt u een andere gebruiken, afhankelijk van wat u nodig hebt.

  1. Ga gewoon door waarden:

    array.each
    
  2. Ga gewoon door indexen:

    array.each_index
    
  3. Ga door indices + indexvariabele:

    for i in array
    
  4. Controlelus Count + Index Variabele:

    array.length.times do | i |
    

7, Autoriteit 2%

Proberen hetzelfde ding consequent met arrays en hashes kunnen gewoon een code-geur zijn, maar voor het risico dat ik worden gebrand? Op zoek naar consistent gedrag, zou dit de truc doen?:

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

8

Ik had geprobeerd een menu te bouwen (in camping en markaby ) met behulp van een hash.

Elk item heeft 2 elementen: een menulabelen een URL, dus een hash leek goed, maar de ‘/’ URL voor ‘Home’ verscheen altijd als laatste ( zoals je zou verwachten voor een hash), dus menu-items verschenen in de verkeerde volgorde.

Het gebruik van een array met each_slicedoet het werk:

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

Extra waarden toevoegen voor elk menu-item (bijv. zoals een CSS IDnaam) betekent gewoon dat de segmentwaarde wordt verhoogd. Dus als een hash, maar met groepen die uit een willekeurig aantal items bestaan. Perfect.

Dit is dus alleen maar om te bedanken voor het per ongeluk hinten naar een oplossing!

Voor de hand liggend, maar het vermelden waard: ik raad aan om te controleren of de lengte van de array deelbaar is door de slice-waarde.


Antwoord 9

Als je de opsombaremixin gebruikt (zoals Rails doet) je kunt iets doen dat lijkt op het vermelde php-fragment. Gebruik gewoon de each_slice methode en maak de hash plat.

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

Minder aap-patches vereist.

Dit veroorzaakt echter wel problemen als je een recursieve array of een hash met arraywaarden hebt. In ruby 1.9 is dit probleem opgelost met een parameter voor de flatten-methode die aangeeft hoe diep recursie moet zijn.

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

Wat betreft de vraag of dit een codegeur is, weet ik niet zeker. Meestal als ik me achterover moet buigen om iets te herhalen, doe ik een stap achteruit en besef ik dat ik het probleem verkeerd aanpak.


Antwoord 10

De juiste manier is degene waarbij u zich het prettigst voelt en die doet wat u wilt. Bij programmeren is er zelden één ‘juiste’ manier om dingen te doen, vaker zijn er meerdere manieren om te kiezen.

Als je je op je gemak voelt met een bepaalde manier om dingen te doen, doe het dan gewoon, tenzij het niet werkt – dan is het tijd om een betere manier te vinden.


Antwoord 11

Het is zinvol om dezelfde methode te gebruiken voor het doorlopen van zowel arrays als hashes, bijvoorbeeld om geneste hash-en-array-structuren te verwerken die vaak het gevolg zijn van parsers, van het lezen van JSON-bestanden enz.

Een slimme manier die nog niet is genoemd, is hoe het wordt gedaan in de Ruby Facets-bibliotheek van standaard bibliotheekextensies. Van hier:

class Array
  # Iterate over index and value. The intention of this
  # method is to provide polymorphism with Hash.
  #
  def each_pair #:yield:
    each_with_index {|e, i| yield(i,e) }
  end
end

Er is al Hash#each_pair, een alias van Hash#each. Dus na deze patch hebben we ook Array#each_pairen kunnen deze door beide hashes en arrays uitwisselbaar gebruiken. Dit fixeert de waargenomen waanzin van de OP die Array#each_with_indexheeft de blokarmen omgekeerd in vergelijking met Hash#each. Voorbeeld Gebruik:

my_array = ['Hello', 'World', '!']
my_array.each_pair { |key, value| pp "#{key}, #{value}" }
# result: 
"0, Hello"
"1, World"
"2, !"
my_hash = { '0' => 'Hello', '1' => 'World', '2' => '!' }
my_hash.each_pair { |key, value| pp "#{key}, #{value}" }
# result: 
"0, Hello"
"1, World"
"2, !"

Other episodes