escaping the .each { } iteration early in Ruby
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.)
BuddyJoe
I like to code C# and work with the web. Still learning.
Updated on July 09, 2022Comments
-
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 over 14 yearsThanks. Answer and +1. Wow I was way off on the initial syntax.
-
Sarah Vessels over 14 yearsYou 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 over 14 yearsThat would work, but I won't always be sure that the source has at least 10 items.
-
visual_learner over 14 yearsAh. You can add
break if items[i] == nil
but at this pointeach_with_index
is looking like what you should be using. -
Chuck over 14 yearsThat will break after the first item.
-
khelll over 14 yearsNot really, how did u test it?
-
Telemachus over 14 yearsShorter:
puts items.take(10)
-
Sarah Vessels over 14 yearsI 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 over 14 yearsI suppose I need to upgrade my Ruby version:
Nothing known about Array#take
-
Telemachus over 14 yearsOr 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 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 over 14 years
Array#take
is present in Ruby 1.8.7 and up. -
Telemachus over 14 yearsOk, now I see it:
puts
returnsnil
when it works. Thus, as long as the index is equal to or less than 9, theputs
happens,nil
is returned and the second half of theand
is not evaluated. When the index hits 10, theputs
doesn't happen and the second half of theand
gets evaluated. At that point, boom:break
. -
Bob Aman over 14 yearsI'd have written it as:
puts items[0..9].join("\n")
-
Chuck over 14 yearsOK, 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
. Theand break
is never executed becauseputs i
is always nil and once c>9, the entire body of the if-statement is no longer executed. Replace thebreak
with(puts "YOU WON'T SEE THIS")
if you want to prove that that branch is never reached. -
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 over 14 yearsRuby 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 over 14 yearsahhh. 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 over 9 yearsI 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 over 6 yearsYou took a simple question and gave a really complicated answer. There's no need to use
throw
andcatch
here or turn this into 13 lines of deeply nested code. Keep it simple. -
Nothus over 6 yearsMy 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.