views:

624

answers:

7

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  A: 

Does this look like what you want?

10.times { |i|
  puts items[i].to_s
}
Chris Lutz
That would work, but I won't always be sure that the source has at least 10 items.
tyndall
Ah. You can add `break if items[i] == nil` but at this point `each_with_index` is looking like what you should be using.
Chris Lutz
+11  A: 

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.

Sarah Vessels
Thanks. Answer and +1. Wow I was way off on the initial syntax.
tyndall
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 http://stackoverflow.com/questions/533008/what-is-the-difference-or-value-of-these-block-coding-styles-in-ruby
Sarah Vessels
A: 
items.each_with_index { |i, c| puts i and break if c <= 9 }
khelll
That will break after the first item.
Chuck
Not really, how did u test it?
khelll
@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.
Telemachus
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`.
Telemachus
The cool thing is that it reads so natural....
khelll
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.
Chuck
@Chuck: thanks for one more round. @Khelll: I think we've proven that it doesn't read very naturally.
Telemachus
+5  A: 

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

Chuck
I'd have written it as: `puts items[0..9].join("\n")`
Bob Aman
Well, the extra `join("\n")` wouldn't change anything as far as I know, so I'm not sure of the point there.
Chuck
+12  A: 

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 }
nimrodm
I have always liked FP, cool!
khelll
Shorter: `puts items.take(10)`
Telemachus
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?
Sarah Vessels
@Sarah: `ri Array#take`
Telemachus
I suppose I need to upgrade my Ruby version: `Nothing known about Array#take`
Chris Lutz
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
`Array#take` is present in Ruby 1.8.7 and up.
Chuck
http://www.ruby-doc.org/core-1.8.7/classes/Array.html#M000341
DigitalRoss
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.
The Wicked Flea
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.
tyndall
+2  A: 

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

Telemachus
+2  A: 

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.

Emily