Rubyの.eachループの終わりを教えてください


91

次のようなループがある場合

users.each do |u|
  #some code
end

ここで、usersは複数のユーザーのハッシュです。ユーザーハッシュの最後のユーザーにいて、その最後のユーザーに対して特定のコードのみを実行したい場合に確認する最も簡単な条件付きロジックは何ですか?

users.each do |u|
  #code for everyone
  #conditional code for last user
    #code for the last user
  end
end

本当にハッシュを意味しますか?ハッシュでの順序付けは必ずしも信頼できるとは限りません(ハッシュに物を追加する方法と使用しているRubyバージョンによって異なります)。順序が信頼できない場合、「最後の」アイテムは一貫性がありません。質問のコードと取得する回答は、配列または列挙可能なものにより適しています。たとえば、ハッシュにはeach_with_indexまたはlastメソッドがありません。
Shadwell

1
HashEnumerableで混合されるため、がありeach_with_indexます。ハッシュキーが並べ替えられていない場合でも、ビューをレンダリングするときにこの種のロジックが常に発生します。データに意味のある意味で実際に「最後」であるかどうかに関係なく、最後のアイテムの表示が異なる場合があります。
ラフォメット2010

もちろん、まったく正しいですがeach_with_index、ハッシュには謝罪があります。うん、それが出てくることがわかります。質問を明確にしようとしているだけです。個人的に私にとって最良の答えはの使用です.lastが、それはハッシュだけには当てはまりません。
Shadwell

Ruby / Railsのループ内マジックの最初と最後のインジケーターが重複していますか?、これが配列ではなくハッシュであることは別として。
Andrew Grimm

1
@Andrewはその完全に関連していることに同意しますが、わずかな素晴らしい小さな答えは、それが正確に重複ではないことを示しています。
サムサフラン2010年

回答:


147
users.each_with_index do |u, index|
  # some code
  if index == users.size - 1
    # code for the last user
  end
end

4
これに関する問題は、条件が毎回実行されることです。.lastループの外側で使用します。
bcackerman 2013

3
私はちょうどあなたがハッシュを反復処理している場合、あなたのようにそれを記述する必要があることを追加したい:users.each_with_index do |(key, value), index| #your code end
HUB

私はそれが全く同じ質問はありません知っているが、このコードは、より良いあなたはそれが私が探していたものだったので、私はupvotingてるので、誰もが、最後のユーザーに何かをしたい場合、私は思う作品
WhiteTiger

38

それはどちらか/または状況だ場合は、すべてのいくつかのコードを適用している場所、しかしに最後のユーザその後、いくつかのユニークなコードだけで、最後のユーザー、他の解決策の一つは、より適切な場合があります。

ただし、すべてのユーザーに対して同じコードを実行し、最後のユーザーに対していくつかの追加コードを実行しているようです。その場合、これはより正しいように思われ、あなたの意図をより明確に述べています。

users.each do |u|
  #code for everyone
end

users.last.do_stuff() # code for last user

2
必要に応じて、条件付きを必要としない場合は+1。(もちろん、私はここでやりました)
ジェレミー

@meagarは、ユーザーを2回ループさせませんか?
アリ

@antいいえ、ループと.lastは関係のない1つのループと1つの呼び出しがあります。
–meagar

@meagarだから、最後に到達するために、最後の要素まで内部的にループしませんか?ループせずに要素に直接アクセスする方法がありますか?
アリ

1
@antいいえ、に関連する内部ループはありません.last。コレクションはすでにインスタンス化されており、配列にアクセスするだけです。コレクションがまだロードされていなくても(たとえば、それはまだハイドレイトされていないActiveRecordリレーションlastでした)、最後の値を取得するためにループすることはありません。これは非常に非効率的です。SQLクエリを変更して、最後のレコードを返すだけです。とは言うものの、このコレクションはすでにによってロードされている.eachため、ロードした場合よりも複雑になることはありませんx = [1,2,3]; x.last
–meagar

20

最善のアプローチは次のとおりです。

users.each do |u|
  #code for everyone
  if u.equal?(users.last)
    #code for the last user
  end
end

22
この回答の問題は、最後のユーザーもリストの前半にいる場合、条件付きコードが複数回呼び出されることです。
evanrmurphy 2012

1
あなたが使用する必要がありu.equal?(users.last)equal?この方法は、OBJECT_ID、ないオブジェクトの値を比較します。ただし、これは記号や数字では機能しません。
Nafaa Boutefer 2016年


6
h = { :a => :aa, :b => :bb }
h.each_with_index do |(k,v), i|
  puts ' Put last element logic here' if i == h.size - 1
end

3

ロジックを2つの部分に分けたほうがよい場合があります。1つはすべてのユーザー用で、もう1つは最後の部分用です。だから私はこのようなことをします:

users[0...-1].each do |user|
  method_for_all_users user
end

method_for_all_users users.last
method_for_last_user users.last

3

@meagerのアプローチは、最後のユーザーを除くすべてのユーザーにコードを適用し、最後のユーザーのみに固有のコードを適用する、どちらかまたは両方の状況でも使用できます。

users[0..-2].each do |u|
  #code for everyone except the last one, if the array size is 1 it gets never executed
end

users.last.do_stuff() # code for last user

このように、条件付きは必要ありません!


@BeniBelaいいえ、[-1]は最後の要素です。[-0]はありません。したがって、最後から2番目は[-2]です。
coderuby

users[0..-2]は正しいですusers[0...-1]。異なる範囲演算子.....参照してくださいstackoverflow.com/a/9690992/67834
エリオット・サイクス

2

別の解決策は、StopIterationから救出することです。

user_list = users.each

begin
  while true do
    user = user_list.next
    user.do_something
  end
rescue StopIteration
  user.do_something
end

4
これは、この問題の良い解決策ではありません。例外は、単純なフロー制御のために悪用されるべきではありません。例外的な状況のためです。
–meagar

@meagarこれは実際にはほとんどかなりクールです。私は、救助されていない例外またはより高い方法に渡す必要がある例外は、例外的な状況にのみ適用されるべきであることに同意します。ただし、これは、イテレータがどこで終了するかについてのルビーのネイティブ知識にアクセスするための優れた(そして明らかに唯一の)方法です。もちろん、別の方法がある場合は、それをお勧めします。私がこれに関して取る唯一の本当の問題は、それがスキップして最初のアイテムに対して何もしないように見えるということです。それを修正すると、不格好の境界を越えてしまいます。
アダマン

2
@meagar「権威からの主張」について申し訳ありませんが、マッツはあなたに同意しません。実際、StopIterationループ出口を処理する正確な理由のために設計されています。。「これは珍しい、例外が(予想終了条件ではなく、予期しない例外的なイベントのために提起されたように見えることがあります。マッツの著書からStopIterationの子孫であるStandardErrorIndexError、それは言葉を持っていない唯一の例外クラスの一つであることに注意してくださいその名前の「エラー」。)Rubyは、この外部反復手法でPythonに従います。(詳細...)
BobRodes

(...)ループの終了を例外として扱うことにより、ループロジックが非常に単純になります。の戻り値でnext特別な反復終了値を確認する必要はありません。また、next?呼び出す前に何らかの述語を呼び出す必要もありませんnext。」
BobRodes

1
@Adamantish ;に遭遇loop doすると暗黙的rescueにあることに注意してくださいStopIteration。これは、Enumeratorオブジェクトを外部で反復するときに特に使用されます。loop do; my_enum.next; end繰り返しmy_enumて最後に終了します。rescue StopIterationそこに入れる必要はありません。(whileまたはを使用する場合は、必ず行う必要がありますuntil。)
BobRodes

0

一部のバージョンのrubyにはハッシュの最後の方法がありません

h = { :a => :aa, :b => :bb }
last_key = h.keys.last
h.each do |k,v|
    puts "Put last key #{k} and last value #{v}" if last_key == k
end

他人の答えを改善する答えですか?「最後の」方法に言及している回答と、この問題を克服するために何を提案するかを投稿してください。
Artemix 2013

がないバージョンのRubyの場合、lastキーのセットは順序付けられていないため、この回答はとにかく間違った/ランダムな結果を返します。
–meagar
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.