Mapping two (or more) arrays in parallel

This is a question of Ruby style & idiom:

I often find myself wanting to map over two (or more) arrays in
parallel. One approach is to zip them together before mapping and then
pull them apart in the body of the call to map():

nuts.zip(bolts).map {|nut, bolt| assemble(nut, bolt)}

or, alternatively:

result = []
nuts.each_with_index {|nut, i| result << assemble(nut, bolts[i]}
result

Neither of these feel especially right: the first case needs to zip
together two arrays, the latter just seems clunky. Is there an elegant
form that you like to use in this case, perhaps by writing a special
iterator?

my approach

1

itr_bolt = bolts.to_enum
p nuts.map{|nut|assemble nut, itr_bolt.next}

2

def parallel(*enums)
return to_enum(callee, *enums) unless block_given?
itrs = enums.map(&:to_enum)

begin
loop do
yield(* itrs.map(&:next))
end
rescue StopIteration
self
end
end

p parallel(nuts, bolts).map{|nut, bolt|assemble nut, bolt}

3

p [].tap{|r|
nuts.zip(bolts){|tuple|r << assemble(*tuple)}
}

run

http://ideone.com/NiJVa

Fearless F. wrote in post #1048783:

Neither of these feel especially right: the first case needs to zip
together two arrays, the latter just seems clunky. Is there an elegant
form that you like to use in this case, perhaps by writing a special
iterator?

I always thought it would be cool if map, select, zip etc. with a block
returned an Enumerator rather than an Array. Then if you want an array
you could add .to_a, but if you didn’t, you could process the results as
they are generated.

Something like this (untested)

module Enumerable
def lazy_zip(*others)
Enumerator.new do |y|
each_with_index do |item, index|
y << ([item] + others.map { |o| o[index] })
end
end
end
end

a = [1,2,3]
b = [4,5,6]
c = [7,8]

a.lazy_zip(b,c) { |row| p row }

Instead of generating a huge result array up-front, the elements are
yielded one at a time.