escaping the .each { } iteration early in Ruby

限于喜欢 提交于 2019-12-09 07:24:20

问题


code:

 c = 0  
 items.each { |i|  
   puts i.to_s    
   # if c > 9 escape the each iteration early - and do not repeat  
   c++  
 }  

I want to grab the first 10 items then leave the "each" loop.

What do I replace the commented line with? is there a better approach? something more Ruby idiomatic?


回答1:


While the break solution works, I think a more functional approach really suits this problem. You want to take the first 10 elements and print them so try

items.take(10).each { |i| puts i.to_s }



回答2:


There is no ++ operator in Ruby. It's also convention to use do and end for multi-line blocks. Modifying your solution yields:

c = 0  
items.each do |i|  
  puts i.to_s    
  break if c > 9
  c += 1 
end

Or also:

items.each_with_index do |i, c|  
  puts i.to_s    
  break if c > 9
end

See each_with_index and also Programming Ruby Break, Redo, and Next.

Update: Chuck's answer with ranges is more Ruby-like, and nimrodm's answer using take is even better.




回答3:


break works for escaping early from a loop, but it's more idiomatic just to do items[0..9].each {|i| puts i}. (And if all you're doing is literally printing the items with no changes at all, you can just do puts items[0..9].)




回答4:


Another option would be

items.first(10).each do |i|
  puts i.to_s
end

That reads a little more easily to me than breaking on an iterator, and first will return only as many items as available if there aren't enough.




回答5:


Another variant:

puts items.first(10)

Note that this works fine with arrays of less than 10 items:

>> nums = (1..5).to_a
=> [1, 2, 3, 4, 5]
>> puts nums.first(10)
1
2
3
4
5

(One other note, a lot of people are offering some form of puts i.to_s, but in such a case, isn't .to_s redundant? puts will automatically call .to_s on a non-string to print it out, I thought. You would only need .to_s if you wanted to say puts 'A' + i.to_s or the like.)




回答6:


Does this look like what you want?

10.times { |i|
  puts items[i].to_s
}



回答7:


items.each_with_index { |i, c| puts i and break if c <= 9 }



回答8:


It was asked:

I want to grab the first 10 items then leave the "each" loop.

Use throw and catch to accomplish this, with few changes to the example:

catch(:done) do
    c = 0
    collected = []
    items.each do |item|
        collected << item
        throw(:done, collected) if c == 9 # started at 0
        c += 1
    end
    collected # if the list is less than 10 long, return what was collected
end

Simply throw the label :done with collected and the catch which is waiting for :done will return collected.

And to "ruby" this up a bit:

catch(:done) do
    items.inject([]) do |collected, item|
        throw(:done, collected) if collected.size == 10
        collected << item # collected gets returned here and populates the first argument of this block
    end
end

I do not know why some people refuse to use inject and use reduce instead (they are equivalent) when clearly the empty array given to inject([]) is being injected with items! Anyhow, the inject will return collected if there are less than 10 items.

Most answers are trying to answer what might be the intent of the question instead of what was asked and items.take(10) does make perfect sense in that case. But I can imagine wanting to grab the first items that fit within my $100 budget. Then you can simply:

catch(:done) do
    items.inject({items: [], budget: 100}) do |ledger, item|
        remainder = ledger[:budget] - item.price
        if remainder < 0
            throw(:done, ledger)
        else
            ledger.tap do |this|
                this[:items] << item
                this[:budget] = remainder
            end # tap just returns what is being tapped into, in this case, ledger
        end
    end
end


来源:https://stackoverflow.com/questions/1568288/escaping-the-each-iteration-early-in-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!