Hakan Ensari Hakan Ensari - 9 months ago 21
Ruby Question

How do I break out of a map/collect and return whatever has been collected up to that point?

I'm rewriting this question in code:

many = 1000

# An expensive method.
# It returns some data or nil if no result is available.
expensive_method = lambda do
rand(5) == 0 ? nil : "foo"

# Now, let's collect some data and stop collecting when no more data is
# available.

# This is concise but doesn't work.
collection = many.times.map do
expensive_method.call || break

puts collection.is_a? Array # false

# This is less concise but works.
collection = []
many.times do
collection << (expensive_method.call || break)

puts collection.is_a? Array # true

# My inner Rubyist ponders: Is it possible to accomplish this more concisely
# using map?


If you really mean "up to the break", [0,1,2,1,0] should result in [0,1], not [0,1,1,0]. The only way in Ruby that I know about is break in a loop. Functional approach could be much slower as you don't actually break:

r = 
  [0,1,2,1,0].inject([true, []]) do |(f, a), i|
    if f
      if i > 1
        [false, a]
        [f, a << i]
      [f, a]
puts r.last.inspect

Compare with:

r = []
[0,1,2,1,0].each do |i|
  break if i > 1
  r << i
puts r.inspect

Tail recursion is out of the question for Ruby, this is how things are done in true functional languages.

Breaking map doesn't work for me, result is nil.

Added: As @dogenpunk pointed out, there is take_while (and drop_while in fact), which is probably a better alternative, only it always creates temporary array which may or may not be the a problem.