109

If I wanted to do something like this:

collection.each do |i|
   return nil if i == 3

   ..many lines of code here..
end

How would I get that effect? I know I could just wrap everything inside the block in a big if statement, but I'd like to avoid the nesting if possible.

Break would not work here, because I do not want to stop iteration of the remaining elements.

Timur Shtatland
  • 12,024
  • 2
  • 30
  • 47
ryeguy
  • 65,519
  • 58
  • 198
  • 260

4 Answers4

218

next inside a block returns from the block. break inside a block returns from the function that yielded to the block. For each this means that break exits the loop and next jumps to the next iteration of the loop (thus the names). You can return values with next value and break value.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • 2
    `break value` didn't work for me, while `next value` worked fine. Thanks a lot. – Francesco Belladonna Sep 21 '13 at 15:10
  • 5
    I'm not sure your definition of next and break are correct. At the very least, they are confusing. break exits the innermost loop, next moves you to the next iteration. source: http://www.tutorialspoint.com/ruby/ruby_loops.htm – WattsInABox Dec 19 '13 at 16:57
  • 5
    @WattsInABox `next` and `break` are in no way specific to looping constructs, so the article you linked is very misleading (since it heavily implies the opposite). And my descriptions are definitely correct when `next` and `break` are used inside blocks (note that `while` and `for` are rarely, if ever, used in Ruby so 99% of uses of `next` and `break` are within blocks). – sepp2k Dec 19 '13 at 21:39
  • I think you mean non-looping blocks. That's what's confusing about it. Also, I disagree with your 99% assertion, but that's a different argument. – WattsInABox Dec 20 '13 at 17:50
  • I was looking for a specific method on the Array class to do because I'm so used to having nice things like .detect, .map, .reduce. I knew I was missing something obvious when I couldn't find anything like that. Thanks. – xander-miller Feb 26 '17 at 15:14
13
#!/usr/bin/ruby

collection = [1, 2, 3, 4, 5 ]

stopped_at = collection.each do |i|
   break i if i == 3

   puts "Processed #{i}"
end

puts "Stopped at and did not process #{stopped_at}"
JD.
  • 3,005
  • 2
  • 26
  • 37
5

In this instance, you can use break to terminate the loop early:

collection.each do |i|
  break if i == 3
  ...many lines
end

...of course, this is assuming that you're not actually looking to return a value, just break out of the block.

Sniggerfardimungus
  • 11,583
  • 10
  • 52
  • 97
  • This would not just return from the block, it would return from each. So the loop would end, not continue with the next value. Not sure that that's what's intended. – sepp2k Mar 25 '10 at 18:17
  • 4
    You can use a value with `break` in Ruby — `break 5` will cause `each` to return 5. – Chuck Mar 25 '10 at 18:21
  • 1
    Thanks, but I don't want to stop iteration. – ryeguy Mar 25 '10 at 18:34
0

Although this is ancient, this still confuses me sometimes. I needed this for a more complicated use case with [].select {|x| }/[].reject {|x| }.

Common Use case

 [1,2,3,4,5].select{|x| x % 2 == 0 }
 => [2, 4] 

But I needed to yield a specific value for each iteration and continue processing

With more complicated logic:

[1,2,3,4,5].select{|x| if x % 2 == 0; next true; end; false }
 => [2, 4] 
# you can also use `next(true)` if it's less confusing

Also, since it's relevant to the thread, using break here will emit the single value you pass in if the conditional hits:

[1,2,3,4,5].select{|x| if x % 2 == 0; break(true); end; false }
 => true 
[1,2,3,4,5].select{|x| if x % 2 == 0; break(false); end; false }
 => false 
[1,2,3,4,5].select{|x| if x % 2 == 0; break('foo'); end; false }
 => "foo" 
Michael
  • 1,786
  • 5
  • 23
  • 42