如何在Ruby循环的第一次迭代中采取不同的行动?


74

我总是使用计数器来检查i==0循环中的第一项():

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

有没有更优雅的方法可以做到这一点(也许是一种方法)?


8
为什么有人投票关闭它?这是一个非常现实的问题。
Telemachus

1
在这种情况下,是什么使您无法对数组中的第一项进行所需的操作,然后each仅对常见内容进行常规循环?
罗素

@Russell如果他对第一项做特殊的事情(用array.first或表示array[0]),然后运行each循环,如果他不想对第一项做常规的事情,他仍然必须测试第一项。
Telemachus

1
是的,但是在上面的示例中,他确实想对第一项做普通的事情。如果他不这样做,他就不能做my_array[1..-1].each吗?
罗素

我认为Russell的意思是处理循环之外的第一项,然后再处理第二项。
Panagiotis Panagi

Answers:


74

你可以这样做:

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

ideone上尝试一下。


1
我认为这可能不是最佳答案-基本上是OP在他的问题中写的内容,但使用.each_with_index代替.each。以下.drop(1)建议似乎更像红宝石,并且完全避免使用索引。
2013年

如果我想跳过前两个迭代怎么办?我尝试if index > 1并得到了错误 undefined method '>' for nil:NilClass
Aaron Franke

46

使用each_with_index,正如其他人所描述的,将做工精细,但对于不同的缘故这里是另一种方法。

如果只想对第一个元素进行特定操作,而对所有元素(包括第一个元素)进行常规操作,则可以执行以下操作:

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

但是,如果您不想对第一个元素做一般性的事情,则可以执行以下操作:

# 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

+1我有相同的想法。唯一的区别是数组为空时。
undur_gongor

13
我喜欢my_array.drop(1)它更具声明性。
tokland

1
它们可以工作,但是在特殊行为处于循环中间的情况下不起作用。在这种情况下,我希望你需要做的each_with_index作为最可读的解决方案..
君代贝茨- Kobashigawa

如果您需要根据集合中的索引对循环中间的元素做一些特殊的事情,请问我是否大胆地说您的代码可能有更大的问题……
Russell 2013年

另外,@ tokland,我将答案更改为use drop。好多了。
罗素

3

数组有一个“ each_with_index”方法,在这种情况下很方便:

my_array.each_with_index do |item, i|
  item.do_something if i==0
  #common stuff
end

对于要渲染其他内容的视图中循环对象的经典情况,这确实非常方便。非常感谢!
阿波罗

3

最适合的取决于情况。

另一个选择(如果您知道数组不为空):

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

2

each_with_indexEnumerable中获取(Enumerable已与Array混合,因此您可以在数组上调用它而没有任何麻烦):

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]

2

如果之后不需要数组:

ar = %w(reversed hello world)

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

#=>REVERSED
#=>olleh
#=>dlrow

与问题中的代码相反,您没有反转第一个单词。
undur_gongor

是的,一定想像过else某个地方。但这确实可以解决问题的标题。
steenslag

1

RubyEnumerable#inject提供了一个参数,可用于在循环的第一次迭代中执行不同的操作:

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

对于诸如sum和product之类的常见事物,该参数不是严格必需的:

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

但是,当你想要做一些不同的在第一次循环,这样的说法可能是对你有用:

> puts fruits.inject("I like to eat: ") {|acc, elem| acc << elem << " "}
I like to eat: apples pears peaches plums oranges 
=> nil

整洁又可爱,因为inject我认为它不能回答OP的问题-这是仅在循环的第一次迭代中如何做一些不同的事情。
罗素

我不inject符合他的要求,但也许您正在看到我们错过的内容。他想通过数组第一次做不同的事情。他i += 1是一个手动计数器,而不是总和。
Telemachus

@罗素,哈!我不得不写文章,inject而忘记了整个论点在第一次迭代中所做的事情是不同的。谢谢。
sarnold

@telemachus,已更新:)感谢您就寝时间的通知。:)
sarnold

0

这是一个不需要立即封闭的解决方案,并且除非确实需要,否则避免了多次指定状态占位符的冗余。

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

它的范围与所有者匹配:$first_time_only在全球范围内一次;@first_time_only对于实例first_time_only将是一次,对于当前作用域将是一次。

如果您想要多次,等等,[1,2,3]如果您需要区分您正在进行的是哪个迭代,或者[1, false, 3, 4]如果您需要一些怪异的东西,则可以很轻松地输入。

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.