Chaining Ruby enumerators

I want to connect two Ruby enumerators. Give me all the values from the first, then the second, and so on. Ideally, without forcing any lazy evaluations and flat so I don’t have to think about nested stuff. Like so:

xs = [1, 2, 3].to_enum
ys = [4, 5, 6].to_enum
[xs, ys].chain.to_a # => [1, 2, 3, 4, 5, 6]

I couldn’t figure out how to do that with Ruby’s standard library alone. But, it wasn’t that hard to write my own:

def chain(*enums)
  return to_enum(:chain, *enums) unless block_given?

  enums.each { |enum| enum.each { |e| yield e } }
 end

But it seems like Ruby’s library, Enumerable in particular, is so strong I must have missed something. So, mob programmers, is there a better way to do this? A fancier enumerator-combining thing I’m missing?

3 responses to “Chaining Ruby enumerators”

  1. Hey, Adam, that’s an interesting problem!

    It seems like you need a lazy enumerator that can abstract multiple enumerables into a single enumerator that lazily provides items from each enumerable, on demand.

    Let me know if this would do the trick: https://github.com/drborges/ruby-spikes/blob/master/lazy_enumerator.rb

    Cheers.

  2. I’m not sure if you would like to avoid using lazy or avoid evaluating anything lazy. If the latter then this might work:

    ns = [xs, ys].lazy.flat_map(&:lazy)
    => #
    ns.force
    => [1, 2, 3, 4, 5, 6]

  3. Well, that first row would have returned an Enumerator::Lazy if it hadn’t been eaten by the comment system.