Ruby Tuesday – Enumerable#zip

Today’s Ruby Tuesday covers Enumerable#zip.

Enumerable#zip weaves together elements of multiple Enumerables. It iterates through each item in the enum and combines it with the corresponding element from each of the arguments passed to zip, resulting in an array of arrays.

[:a, :b, :c].zip([1, 2, 3])
# => [[:a, 1], [:b, 2], [:c, 3]]
[:a, :b, :c].zip([1, 2, 3], [:foo, :bar, :baz])
# => [[:a, 1, :foo], [:b, 2, :bar], [:c, 3, :baz]]

If an item in the argument list is shorter then the enum that zip is being called on, nil is used where the item would have been added.

[:a, :b, :c].zip([1], [:foo, :bar, :baz, :snafu])
# => [[:a, 1, :foo], [:b, nil, :bar], [:c, nil, :baz]]

The length of the returned array is the length of the enum that zip is being called on. Any extra elements in the enumerable passed to zip are ignored, and if are in a lazy enumerble not computed.

[:a, :b, :c].zip([1, 2, 3, 4], [:foo, :bar, :baz, :snafu])
# => [[:a, 1, :foo], [:b, 2, :bar], [:c, 3, :baz]]

### Only takes what is needed ###
require 'benchmark'
# => true
Benchmark.measure do
  [:a, :b].zip( (1..1_000_000_000_000).lazy.map{|x| x*x*x} ) 
end
# => #<Benchmark::Tms:0x007fd5749d9288
#  @cstime=0.0,
#  @cutime=0.0,
#  @label="",
#  @real=4.3e-05,
#  @stime=0.0,
#  @total=0.0,
#  @utime=0.0>

#### For much much less, took 8+ seconds ####
Benchmark.measure do
  [:a, :b].zip( (1..10_000_000).map{|x| x*x*x} )   
end
# => #<Benchmark::Tms:0x007faed4779e00
#  @cstime=0.0,
#  @cutime=0.0,
#  @label="",
#  @real=8.616259,
#  @stime=0.7200000000000002,
#  @total=8.53,
#  @utime=7.809999999999999>

Enumerable#zip can also be given a block. If a block is provided, the combined array of items is passed to the block, and not returned as a result. This behavior is distinct from Enumerable#each in that each still returns the Enumerable it was invoked on, where as zip returns nil as it’s result when a block is given.

[:a, :b, :c].zip([1, 2, 3, 4], [:foo, :bar, :baz, :snafu]) { |items| puts items.to_s }
# [:a, 1, :foo]
# [:b, 2, :bar]
# [:c, 3, :baz]
# => nil
[:a, :b, :c].zip([1, 2, 3, 4], [:foo, :bar, :baz, :snafu]).each { |items| puts items.to_s }
# [:a, 1, :foo]
# [:b, 2, :bar]
# [:c, 3, :baz]
# => [[:a, 1, :foo], [:b, 2, :bar], [:c, 3, :baz]]

–Proctor