escaping the .each { } iteration early in Ruby

61,671

Solution 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 }

Solution 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.

Solution 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].)

Solution 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.

Solution 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.)

Share:
61,671
BuddyJoe
Author by

BuddyJoe

I like to code C# and work with the web. Still learning.

Updated on July 09, 2022

Comments

  • BuddyJoe
    BuddyJoe almost 2 years

    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?

  • BuddyJoe
    BuddyJoe over 14 years
    Thanks. Answer and +1. Wow I was way off on the initial syntax.
  • Sarah Vessels
    Sarah Vessels over 14 years
    You weren't far off, really: the only invalid part of your answer was the ++. Curly braces for blocks will work, it's just not preferred for multi-line blocks; see stackoverflow.com/questions/533008/…
  • BuddyJoe
    BuddyJoe over 14 years
    That would work, but I won't always be sure that the source has at least 10 items.
  • visual_learner
    visual_learner over 14 years
    Ah. You can add break if items[i] == nil but at this point each_with_index is looking like what you should be using.
  • Chuck
    Chuck over 14 years
    That will break after the first item.
  • khelll
    khelll over 14 years
    Not really, how did u test it?
  • Telemachus
    Telemachus over 14 years
    Shorter: puts items.take(10)
  • Sarah Vessels
    Sarah Vessels over 14 years
    I tried Googling for 'ruby take method' just now and couldn't find a reference to what module take is in. Where is it in the API?
  • visual_learner
    visual_learner over 14 years
    I suppose I need to upgrade my Ruby version: Nothing known about Array#take
  • Telemachus
    Telemachus over 14 years
    Or ri Enumerable#take, I suppose. Note that it appears to be new in Ruby 1.9. I get a no method error in Ruby 1.8.6 trying it.
  • Telemachus
    Telemachus over 14 years
    @Khelll: is this due to lazy evaluation of and? It works, but it's a bit too clever for me. My brain keeps wanting >= since I see "and break if" together.
  • Chuck
    Chuck over 14 years
    Array#take is present in Ruby 1.8.7 and up.
  • Telemachus
    Telemachus over 14 years
    Ok, now I see it: puts returns nil when it works. Thus, as long as the index is equal to or less than 9, the puts happens, nil is returned and the second half of the and is not evaluated. When the index hits 10, the puts doesn't happen and the second half of the and gets evaluated. At that point, boom: break.
  • Bob Aman
    Bob Aman over 14 years
    I'd have written it as: puts items[0..9].join("\n")
  • Chuck
    Chuck over 14 years
    OK, I had it backwards before, but it's still wrong: This never breaks. Look at it like this: if c <= 9; puts i; break; end. The and break is never executed because puts i is always nil and once c>9, the entire body of the if-statement is no longer executed. Replace the break with (puts "YOU WON'T SEE THIS") if you want to prove that that branch is never reached.
  • Telemachus
    Telemachus over 14 years
    @Chuck: thanks for one more round. @Khelll: I think we've proven that it doesn't read very naturally.
  • Robert K
    Robert K over 14 years
    Ruby 1.8.7 has it, otherwise you can monkey-patch it into Array: class Array; def take(how_many); self[0..how_many]; end. AFAIK this is simple and reliable.
  • BuddyJoe
    BuddyJoe over 14 years
    ahhh. I'm down with FP. "You know me!". +1 and answer. I like this better than previous answer. But good to know the break option is there. break lines are easy to comment out during testing.
  • chrisallick
    chrisallick over 9 years
    I like your first solution because if you want to loop over 100 items every time but only take out 10 conditionally you can increment the counter independently.
  • NeuroXc
    NeuroXc over 6 years
    You took a simple question and gave a really complicated answer. There's no need to use throw and catch here or turn this into 13 lines of deeply nested code. Keep it simple.
  • Nothus
    Nothus over 6 years
    My answer is 6 lines, it shows an alternative way to escape an each loop which was asked, it is nested one level deeper than most of the answers. My hope in leaving this answer was to show how to alternatively get out of a loop taking advantage of this simple context. If you had actually read my post, my 13 lines of code is a more complex answer to a more complex example I posed in my answer. I apologize in advance for having too many words in this response.