Ruby'de bir .each döngüsünün sonunu söyle


91

Gibi bir döngüm varsa

users.each do |u|
  #some code
end

Kullanıcılar, birden çok kullanıcının karmasıdır. Kullanıcı karmasındaki son kullanıcıda olup olmadığınızı ve yalnızca o son kullanıcı için belirli bir kodu yürütmek isteyip istemediğinizi görmenin en kolay koşullu mantığı nedir, bu nedenle

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

Gerçekten bir esrar mı demek istiyorsun? Bir hash üzerinde sipariş vermek her zaman güvenilir değildir (hash'e nasıl bir şeyler eklediğinize ve hangi Ruby sürümünü kullandığınıza bağlı olarak). Güvenilir olmayan siparişlerde 'son' ürün tutarlı olmayacaktır. Sorudaki kod ve aldığınız yanıtlar bir dizi veya numaralandırılabilir için daha uygundur. Örneğin, hash'lerin each_with_indexveya lastyöntemleri yoktur.
Shadwell

1
HashEnumerable'da mixes, bu yüzden sahip each_with_index. Karma anahtarlar sıralanmamış olsa bile, bu tür bir mantık, görünümler işlenirken her zaman ortaya çıkar; burada son öğe, herhangi bir veri açısından anlamlı anlamda "son" olup olmadığına bakılmaksızın farklı bir şekilde görüntülenebilir.
Raphomet

Tabii ki, oldukça doğru, hash'lerin var each_with_index, özürler. Evet, ortaya çıkacağını görebiliyorum; sadece soruyu açıklığa kavuşturmaya çalışıyorum. Şahsen benim için en iyi cevap, .lastkarma bir dizinin kullanılmasıdır, ancak bu sadece bir dizi için geçerli değildir.
Shadwell

Kopyası Ruby / Rails bir döngü Sihirli Birinci ve Son Göstergesi? , bunun bir diziden ziyade bir hash olması dışında.
Andrew Grimm

1
@Andrew tamamen ilgili olduğuna katılıyor, ancak harika küçük cevaplar bunun tam olarak bir dupe olmadığını gösteriyor.
Sam Saffron

Yanıtlar:


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

4
Bununla ilgili sorun, her seferinde bir koşullu koşturmadır. .lastdöngünün dışında kullanın .
bcackerman

3
Bir hash üzerinde yineleme yapıyorsanız, bunu şöyle yazmanız gerektiğini eklemeliyim:users.each_with_index do |(key, value), index| #your code end
HUB

Bunun tamamen aynı soru olmadığını biliyorum, ancak bu kod daha iyi çalışıyor Bence son kullanıcı AMA herkese bir şey yapmak istiyorsanız, bu yüzden aradığım şey buydu
WhiteTiger

38

Eğer tüm için bazı kod uyguluyorsanız bir ya / veya durum buysa ama son kullanıcı ve sonra bazı benzersiz bir kod sadece son kullanıcı, diğer çözümlerden biri daha uygun olabilir.

Ancak, tüm kullanıcılar için aynı kodu ve son kullanıcı için bazı ek kodları çalıştırdığınız görülüyor . Durum buysa, bu daha doğru görünüyor ve amacınızı daha açık bir şekilde ifade ediyor:

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

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

2
Uygunsa, şarta ihtiyaç duymamak için +1. (tabii ki, burada yaptım)
Jeremy

@meagar bu döngü kullanıcılara iki kez geçmeyecek mi?
karınca

@ant Hayır, döngü ile .lastilgisi olmayan bir döngü ve bir çağrı var.
meagar

@meagar yani son öğeye ulaşmak için son öğeye kadar dahili olarak döngü yapmıyor mu? döngüye gerek kalmadan doğrudan öğeye erişmenin bir yolu var mı?
karınca

1
@ant Hayır, dahil olan dahili bir döngü yok .last. Koleksiyon halihazırda somutlaştırılmıştır, bu sadece bir diziye basit bir erişimdir. Koleksiyon önceden yüklenmemiş olsa bile (olduğu gibi, hala susuz bir ActiveRecord ilişkisiydi) , son değeri almak için lasthala hiçbir zaman döngü yapmıyor , bu çılgınca verimsiz olurdu. Son kaydı döndürmek için basitçe SQL sorgusunu değiştirir. Bununla birlikte, bu koleksiyon zaten tarafından yüklendi .each, bu yüzden sizin yaptığınızdan daha fazla karmaşıklık yok x = [1,2,3]; x.last.
meagar

20

Bence en iyi yaklaşım:

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

22
Bu cevapla ilgili sorun, eğer son kullanıcı da listede daha önce ortaya çıkarsa, koşullu kodun birden çok kez çağrılacağıdır.
evanrmurphy

1
kullanmak zorundasınız u.equal?(users.last), equal?yöntem nesnenin değerini değil, object_id'yi karşılaştırır. Ancak bu, semboller ve sayılarla çalışmayacaktır.
Nafaa Boutefer

11

Denedin each_with_indexmi

users.each_with_index do |u, i|
  if users.size-1 == i
     #code for last items
  end
end

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

Bazen mantığı iki kısma ayırmayı daha iyi buluyorum, biri tüm kullanıcılar için diğeri sonuncusu için. Bu yüzden şöyle bir şey yapardım:

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'in yaklaşımını, son kullanıcı dışında herkese bazı kodlar uyguladığınız ve ardından yalnızca son kullanıcıya bazı benzersiz kodlar uyguladığınız bir / veya durum için de kullanabilirsiniz.

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

Bu şekilde bir şarta ihtiyacınız yok!


@BeniBela Hayır, [-1] son ​​unsurdur. [-0] yoktur. Yani sondan ikinci [-2].
coderuby

users[0..-2]olduğu gibi doğrudur users[0...-1]. Farklı aralık operatörleri Not ..vs ...bkz stackoverflow.com/a/9690992/67834
Eliot Sykes

2

Başka bir çözüm de StopIteration'dan kurtarmaktır:

user_list = users.each

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

4
Bu, bu soruna iyi bir çözüm değil. İstisnalar basit akış kontrolü için kötüye kullanılmamalıdır, istisnai durumlar içindir.
meagar

@meagar Bu aslında neredeyse oldukça havalı. Kurtarılmamış istisnaların veya daha yüksek bir yönteme geçilmesi gereken istisnaların yalnızca istisnai durumlar için olması gerektiğini kabul ediyorum . Bununla birlikte, bu, Ruby'nin bir yineleyicinin nerede bittiğine dair yerel bilgisine erişmenin düzgün (ve görünüşe göre tek) bir yoludur. Tabii ki tercih edilen başka bir yol varsa. Bununla alacağım tek gerçek sorun, ilk öğe için atladığı ve hiçbir şey yapmadığı görülüyor. Bunu düzeltmek, beceriksizliğin sınırlarını aşar.
Adamantish

2
@meagar "Otoriteden gelen bir tartışma" için özür dilerim ama Matz sana katılmıyor. Aslında, StopIterationdöngü çıkışlarını ele almanın kesin nedeni için tasarlanmıştır. Matz'ın kitabından: "Bu alışılmadık görünebilir - beklenmedik ve istisnai bir olaydan ziyade beklenen bir sonlandırma koşulu için bir istisna gündeme getirilmiştir. ( StopIterationNeslinin altındadır StandardErrorve IndexError; kelimesine sahip olmayan tek istisna sınıflarından biridir
Adında

(...) Döngü sonlandırmayı bir istisna olarak ele alarak, döngü mantığınızı son derece basit hale getirir; nextözel bir yineleme sonu değeri için dönüş değerini kontrol etmeye gerek yoktur ve aramadan next?önce bir tür next
koşulu

1
@Adamantish Bir ile karşılaştığında loop doörtük olduğu unutulmamalıdır ; özellikle bir nesneyi harici olarak yinelemek için kullanılır . sonunda yinelenecek ve çıkacak ; oraya bir koymaya gerek yok. (Eğer kullanırsanız zorunda ya .)rescueStopIterationEnumeratorloop do; my_enum.next; endmy_enumrescue StopIterationwhileuntil
BobRodes

0

Ruby'nin bazı sürümleri için hash için son bir yöntem yoktur

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

Başka birinin cevabını geliştiren bir cevap mı? Lütfen hangi yanıtın 'son' yöntemden bahsettiğini ve bu sorunu aşmak için ne önerdiğinizi belirtin.
Artemix

Ruby'nin a içermeyen sürümleri için last, anahtarlar sırasız olacaktır, bu nedenle bu cevap yine de yanlış / rastgele sonuçlar verecektir.
meagar
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.