告诉红宝石.each循环的结尾


90

如果我有一个循环

users.each do |u|
  #some code
end

用户是多个用户的哈希。查看您是否位于用户哈希中的最后一个用户并且只想为该最后一个用户执行特定代码的最简单的条件逻辑是什么

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

你真的是说哈希吗?对哈希进行排序并不总是可靠的(取决于您如何向哈希添加内容以及所使用的红宝石版本)。如果订购不可靠,则“最后”商品将不一致。问题中的代码和得到的答案更适合数组或可枚举。例如,哈希没有each_with_indexlast方法。
Shadwell

1
Hash在Enumerable中混合,因此它确实具有each_with_index。即使没有对哈希键进行排序,在呈现视图时也始终会出现这种逻辑,无论在某种意义上的数据意义上,最后一项是否实际上是“最后”,其最后一项都可能以不同的方式显示。
Raphomet

当然,很正确,哈希确实有each_with_index道歉。是的,我可以看到它会出现;只是试图澄清这个问题。就我个人而言,最好的答案是使用,.last但不适用于仅适用于数组的哈希。
Shadwell


1
@Andrew同意它的完全相关性,但是微不足道的小回答表明它并不完全是骗子。
山姆藏红花

Answers:


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

4
问题是每次都有条件运行。.last在循环外使用。
bcackerman

3
我要补充一点,如果您要遍历哈希,则必须这样写:users.each_with_index do |(key, value), index| #your code end
HUB 2014年

我知道这不是完全相同的问题,但是这段代码可以更好地发挥作用,我想如果您想对每个用户都做点什么,但最后一个用户,那我就很讨厌,因为那是我一直在寻找的东西
WhiteTiger

38

如果这是一个非此即彼/或情况,在那里你将一些代码给所有,但最后一个用户,然后一些独特的代码,只有最后一个用户的另一种解决方案可能更为合适。

但是,您似乎正在为所有用户运行相同的代码,并为最后一个用户运行一些其他代码。如果是这样,这似乎更正确,并且更清楚地说明了您的意图:

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

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

2
+1(如果合适),不需要条件。(当然,我在这里做了)
杰里米(Jeremy

@meagar不会让用户两次进入循环吗?
蚂蚁2015年

@ant不,有一个循环和一个.last与循环无关的调用。
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

1
您必须使用u.equal?(users.last),该equal?方法比较object_id而不是对象的值。但这不适用于符号和数字。
纳法阿·布特费尔


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

有时,我发现将逻辑分为两部分更好,一个用于所有用户,一个用于最后一个。所以我会做这样的事情:

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]。
coderuby

users[0..-2]是正确的users[0...-1]。请注意不同的范围运算符..vs ...,请参见stackoverflow.com/a/9690992/67834
Eliot Sykes,

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这实际上几乎很酷。我同意未解决的异常或需要传递给更高方法的异常仅应用于特殊情况。但是,这是一种获取Ruby迭代器结束位置的本机知识的简洁(且显然只是)方法。如果有另一种方式,当然是首选。我要解决的唯一真正的问题是,它似乎跳过而对第一项不做任何事情。解决该问题将使其超出笨拙的边界。
精金

2
@meagar对不起,“来自权威的争论”,但Matz与您不同意。实际上,StopIteration是出于处理循环退出的确切原因而设计的。根据Matz的书中的说法:“这似乎很不正常-为预期的终止条件引发了异常,而不是意外的异常事件。(StopIterationStandardErrorand的后代IndexError;请注意,它是唯一不包含单词的异常类之一Ruby在这种外部迭代技术中遵循Python(更多...)
BobRodes

(...)通过将循环终止视为异常,它使循环逻辑变得非常简单;无需检查返回值是否next为特殊的迭代结束值,也无需在调用next?之前调用某种谓词next。”
BobRodes

1
@Adamantish应该注意的是,遇到时loop do有一个隐含rescueStopIteration;它在外部迭代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

对于没有的Ruby版本,last键集将是无序的,因此无论如何此答案都将返回错误/随机结果。
meagar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.