在 ruby 中告诉 .each 循环的结束
Posted
技术标签:
【中文标题】在 ruby 中告诉 .each 循环的结束【英文标题】:Tell the end of a .each loop in ruby 【发布时间】:2011-04-29 09:07:24 【问题描述】:如果我有一个循环,例如
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
方法。
Hash
混合在 Enumerable 中,所以它确实有 each_with_index
。即使哈希键没有排序,这种逻辑在渲染视图时总是会出现,最后一项可能会以不同的方式显示,无论它在任何数据意义意义上是否实际上是“最后一项”。
当然,非常正确,哈希确实有each_with_index
,抱歉。是的,我可以看到它会出现;只是想澄清这个问题。就我个人而言,最好的答案是使用.last
,但这不适用于仅数组的哈希。
Magic First and Last Indicator in a Loop in Ruby/Rails? 的重复,除了这是一个哈希而不是一个数组。
@Andrew 同意它完全相关,但是 meagars 很棒的小答案表明它并不完全是一个骗局。
【参考方案1】:
我认为最好的方法是:
users.each do |u|
#code for everyone
if u.equal?(users.last)
#code for the last user
end
end
【讨论】:
这个答案的问题是,如果最后一个用户也出现在列表的前面,那么条件代码将被多次调用。 你必须使用u.equal?(users.last)
,equal?
方法比较的是object_id,而不是object的值。但这不适用于符号和数字。【参考方案2】:
您也可以将@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]。users[0..-2]
是正确的,users[0...-1]
也是正确的。请注意不同的范围运算符 ..
与 ...
,请参阅 ***.com/a/9690992/67834【参考方案3】:
有时我发现将逻辑分成两部分更好,一部分用于所有用户,另一部分用于最后一个。所以我会做这样的事情:
users[0...-1].each do |user|
method_for_all_users user
end
method_for_all_users users.last
method_for_last_user users.last
【讨论】:
【参考方案4】:另一种解决方案是从 StopIteration 中拯救:
user_list = users.each
begin
while true do
user = user_list.next
user.do_something
end
rescue StopIteration
user.do_something
end
【讨论】:
这不是解决这个问题的好办法。异常不应被滥用于简单的流控制,它们用于异常情况。 @meagar 这实际上非常酷。我同意 unrescued 异常或需要传递给更高方法的异常应该只用于特殊情况。然而,这是获取 ruby 对迭代器结束位置的原生知识的一种简洁(并且显然是唯一的)方法。如果有另一种方式,当然是首选。我对此唯一真正的问题是它似乎跳过并且对第一项不做任何事情。解决这个问题会超越笨拙的界限。 @meagar 很抱歉“来自权威的争论”,但 Matz 不同意你的观点。事实上,StopIteration
是为处理循环退出的确切原因而设计的。来自 Matz 的书中:“这可能看起来不寻常——针对预期的终止条件而不是意外和异常事件引发异常。(StopIteration
是 StandardError
和 IndexError
的后代;请注意,它是只有名称中没有“error”一词的异常类。)Ruby 在这种外部迭代技术中遵循 Python。(更多...)
(...) 通过将循环终止视为异常,它使您的循环逻辑非常简单;不需要检查next
的返回值是否有特殊的迭代结束值,也不需要在调用next
之前调用某种next?
谓词。"
@Adamantish 需要注意的是loop do
在遇到StopIteration
时有一个隐含的rescue
;它专门用于在外部迭代 Enumerator
对象时使用。 loop do; my_enum.next; end
将迭代 my_enum
并在最后退出;无需在其中输入rescue StopIteration
。 (如果您使用while
或until
,则必须这样做。)【参考方案5】:
对于某些版本的 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
【讨论】:
这是一个可以改善别人答案的答案吗?请发布哪个答案提到了“最后一个”方法,以及您打算如何解决这个问题。 对于没有last
的 Ruby 版本,键集将是无序的,所以这个答案无论如何都会返回错误/随机结果。【参考方案6】:
如果是非此即彼的情况,您将一些代码应用于所有但最后一个用户,然后将一些唯一代码应用于仅最后一个用户,一个其他解决方案可能更合适。
但是,您似乎为所有用户运行相同的代码,并为最后一个用户运行一些附加代码。如果是这样的话,这似乎更正确,并且更清楚地表明了您的意图:
users.each do |u|
#code for everyone
end
users.last.do_stuff() # code for last user
【讨论】:
+1 表示不需要条件,如果合适的话。 (当然,我在这里做过) @meagar 不会让用户循环两次吗? @ant 不,有一个循环和一个对.last
的调用,与循环无关。
@meagar 所以为了到达最后一个元素,它不会在内部循环直到最后一个元素?它有一种无需循环就可以直接访问元素的方法吗?
@ant 不,.last
不涉及内部循环。集合已经实例化,它只是对数组的简单访问。即使该集合尚未加载(例如,它仍然是一个未水合的 ActiveRecord 关系)last
仍然永远不会 循环 来获取最后一个值,这将是非常低效的。它只是修改 SQL 查询以返回最后一条记录。也就是说,这个集合已经被.each
加载了,所以不会比你加载x = [1,2,3]; x.last
更复杂。【参考方案7】:
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
【讨论】:
【参考方案8】:你试过each_with_index
吗?
users.each_with_index do |u, i|
if users.size-1 == i
#code for last items
end
end
【讨论】:
【参考方案9】:users.each_with_index do |u, index|
# some code
if index == users.size - 1
# code for the last user
end
end
【讨论】:
这个问题是每次都运行条件。在循环外使用.last
。
我想补充一点,如果你要遍历一个哈希,你必须这样写:users.each_with_index do |(key, value), index| #your code end
我知道这不完全是同一个问题,但是这段代码效果更好以上是关于在 ruby 中告诉 .each 循环的结束的主要内容,如果未能解决你的问题,请参考以下文章
通过 for 循环,比较 Python 与 Ruby 编程思想的差别