Wie verhalte ich mich bei der ersten Iteration in einer Ruby-Schleife anders?

74

Ich benutze immer einen Zähler, um nach dem ersten item ( i==0) in einer Schleife zu suchen:

i = 0
my_array.each do |item|
  if i==0
    # do something with the first item
  end
  # common stuff
  i += 1
end

Gibt es eine elegantere Möglichkeit, dies zu tun (vielleicht eine Methode)?

Panagiotis Panagi
quelle
8
Warum sollte jemand dafür stimmen, dies zu schließen? Das ist eine sehr reale Frage.
Telemachos
1
Was hindert Sie in diesem Fall daran, mit dem ersten Element im Array das zu tun, was Sie benötigen, und dann eine normale eachSchleife mit nur den üblichen Dingen durchzuführen?
Russell
@Russell Wenn er das Besondere am ersten Gegenstand tut (z. B. mit array.firstoder array[0]) und dann die eachSchleife ausführt , muss er immer noch auf den ersten Gegenstand testen, wenn er nicht auch das reguläre Ding mit dem ersten Gegenstand machen möchte.
Telemachos
1
Ja , aber in dem obigen Beispiel er nicht die gemeinsame Sachen zu dem ersten Punkt machen will. Und wenn nicht, konnte er es nicht einfach tun my_array[1..-1].each?
Russell
Ich denke, Russell bedeutet, sich mit dem ersten Element außerhalb der Schleife und dann mit der Schleife vom zweiten Element zu befassen.
Panagiotis Panagi

Antworten:

74

Du kannst das:

my_array.each_with_index do |item, index|
    if index == 0
        # do something with the first item
    end
    # common stuff
end

Probieren Sie es auf ideone .

verstimmt
quelle
1
Ich denke, dies ist vielleicht nicht die beste Antwort - es ist im Grunde das, was der OP in seiner Frage geschrieben hat, aber .each_with_indexanstelle von .each. Der folgende .drop(1)Vorschlag wirkt eher rubinrot und vermeidet Indizes insgesamt.
jm3
Was ist, wenn ich die ersten beiden Iterationen überspringen möchte? Ich habe versucht if index > 1und den Fehler bekommen undefined method '>' for nil:NilClass
Aaron Franke
46

Die Verwendung each_with_index, wie andere beschrieben haben, würde gut funktionieren, aber aus Gründen der Abwechslung ist hier ein anderer Ansatz.

Wenn Sie etwas Spezifisches nur für das erste Element und etwas Allgemeines für alle Elemente einschließlich des ersten tun möchten, können Sie Folgendes tun:

# do something with my_array[0] or my_array.first
my_array.each do |e| 
  # do the same general thing to all elements 
end

Aber wenn Sie das Allgemeine mit dem ersten Element nicht tun möchten, können Sie Folgendes tun:

# do something with my_array[0] or my_array.first
my_array.drop(1).each do |e| 
  # do the same general thing to all elements except the first 
end
Russell
quelle
+1 Ich hatte die gleiche Idee. Der einzige Unterschied besteht darin, dass das Array leer ist.
undur_gongor
13
Ich mag my_array.drop(1)es ist deklarativer.
Tokand
1
Diese funktionieren, jedoch nicht in Situationen, in denen sich das spezielle Verhalten in der Mitte der Schleife befindet. In diesem Fall erwarte ich, dass Sie each_with_index als die am besten lesbare Lösung verwenden müssen.
Jun-Dai Bates-Kobashigawa
Wenn Sie etwas Besonderes mit einem Element in der Mitte einer Schleife tun müssen, basierend auf seinem Index in der Sammlung, könnte ich so mutig sein, zu sagen, dass Ihr Code wahrscheinlich größere Probleme hat ...
Russell
Außerdem, @tokland, habe ich die zu verwendende Antwort geändert drop. Es ist viel schöner.
Russell
3

Arrays haben eine "each_with_index" -Methode, die für diese Situation praktisch ist:

my_array.each_with_index do |item, i|
  item.do_something if i==0
  #common stuff
end
Toby Hede
quelle
Dies ist sehr praktisch für die klassische Situation, ein Objekt in einer Ansicht zu schleifen, wenn Sie etwas anderes rendern möchten. Danke vielmals!
Apollo
3

Was am besten passt, hängt von der jeweiligen Situation ab.

Eine weitere Option (wenn Sie wissen, dass Ihr Array nicht leer ist):

# treat the first element (my_array.first)
my_array.each do | item |
   # do the common_stuff
end
undur_gongor
quelle
2

each_with_indexvon Enumerable (Enumerable ist bereits mit Array gemischt, sodass Sie es problemlos auf einem Array aufrufen können):

irb(main):001:0> nums = (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):003:0> nums.each_with_index do |num, idx|
irb(main):004:1* if idx == 0
irb(main):005:2> puts "At index #{idx}, the number is #{num}."
irb(main):006:2> end
irb(main):007:1> end
At index 0, the number is 1.
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Telemachos
quelle
2

Wenn Sie das Array danach nicht benötigen:

ar = %w(reversed hello world)

puts ar.shift.upcase
ar.each{|item| puts item.reverse}

#=>REVERSED
#=>olleh
#=>dlrow
steenslag
quelle
Im Gegensatz zum Code in der Frage kehren Sie das erste Wort nicht um.
undur_gongor
Ja, muss sich elseirgendwo etwas vorgestellt haben. Aber es macht das, was der Titel der Frage sagt.
Steenslag
1

Ruby's Enumerable#injectbietet ein Argument, mit dem Sie bei der ersten Iteration einer Schleife etwas anderes tun können:

> l=[1,2,3,4]
=> [1, 2, 3, 4]
> l.inject(0) {|sum, elem| sum+elem}
=> 10

Das Argument ist für gängige Dinge wie Summen und Produkte nicht unbedingt erforderlich:

> l.inject {|sum, elem| sum+elem}
=> 10

Wenn Sie jedoch bei der ersten Iteration etwas anderes tun möchten , kann dieses Argument für Sie hilfreich sein:

> puts fruits.inject("I like to eat: ") {|acc, elem| acc << elem << " "}
I like to eat: apples pears peaches plums oranges 
=> nil
Sarnold
quelle
Ordentlich und liebenswert, wie injectich glaube, es beantwortet nicht die Frage des OP - wie man etwas anderes nur für die erste Iteration der Schleife macht.
Russell
Ich injectpasse nicht zu dem, wonach er fragt, aber vielleicht sehen Sie etwas, das wir verpasst haben. Er möchte beim ersten Mal etwas anderes durch ein Array machen. Sein i += 1ist ein manueller Zähler, keine Summe.
Telemachos
@Russel, ha! Ich musste darüber schreiben injectund vergaß, dass der springende Punkt des Arguments bei der ersten Iteration etwas anderes war. Vielen Dank.
Sarnold
@ Telemachus, aktualisiert :) Danke für den Hinweis, dass es Schlafenszeit ist. :)
Sarnold
0

Hier ist eine Lösung, die sich nicht in einer sofort einschließenden Schleife befinden muss und die Redundanz vermeidet, einen Statusplatzhalter mehrmals anzugeben, es sei denn, Sie müssen dies wirklich tun.

do_this if ($first_time_only ||= [true]).shift

Sein Umfang entspricht dem Inhaber: $first_time_onlywird global einmal sein; @first_time_onlywird einmal für die Instanz und first_time_onlyeinmal für den aktuellen Bereich sein.

Wenn Sie die ersten mehrmals möchten, können Sie leicht sagen, [1,2,3]ob Sie unterscheiden müssen, in welcher der ersten Iterationen Sie sich befinden, oder sogar etwas Besonderes, [1, false, 3, 4]wenn Sie etwas Seltsames benötigen.

android.weasel
quelle