Array#zip

Added in v1.8.7 · Updated April 1, 2026 · Array Methods
ruby array zip combine stdlib

What does zip do?

zip combines two or more arrays element-by-element, producing an array of tuples. Each tuple contains one element from each input array at the same index position.

Think of it as a zipper on a jacket — it pairs teeth from two sides in order.

Array#zip is defined on the Array class, but the implementation lives in Enumerable#zip. Array includes Enumerable, so Array inherits it. The behaviour is identical whether you call it on an Array or any other Enumerable.

Basic Usage

Combining two arrays

numbers = [1, 2, 3]
letters = ['a', 'b', 'c']

numbers.zip(letters)
# => [[1, "a"], [2, "b"], [3, "c"]]

With a single array argument

[10, 20, 30].zip([100, 200, 300])
# => [[10, 100], [20, 200], [30, 300]]

Without a block

Called without a block, zip returns a new array of arrays (an Enumerator if no arguments):

[1, 2, 3].zip                      # => #<Enumerator: [1, 2, 3]:zip>
[1, 2, 3].zip(['a', 'b', 'c'])     # => [[1, "a"], [2, "b"], [3, "c"]]

Zipping Multiple Arrays

You can pass any number of arrays:

ids    = [1, 2, 3]
names  = ['Alice', 'Bob', 'Carol']
scores = [95, 87, 92]

ids.zip(names, scores)
# => [[1, "Alice", 95], [2, "Bob", 87], [3, "Carol", 92]]

Handling Different-Length Arrays

When arrays have different lengths, zip stops at the shortest one. Extra elements from longer arrays are discarded, and shorter arrays fill in with nil:

short = [1, 2]
long  = ['a', 'b', 'c', 'd']

short.zip(long)
# => [[1, "a"], [2, "b"]]

# Reversing the order changes which elements survive
long.zip(short)
# => [["a", 1], ["b", 2], ["c", nil], ["d", nil]]

Filling short arrays with a default value

If you need a value other than nil, pad the shorter array first:

a = [1, 2, 3, 4]
b = ['x', 'y']

padded_b = b + [nil] * (a.length - b.length)
a.zip(padded_b)
# => [[1, "x"], [2, "y"], [3, nil], [4, nil]]

With a Block

Pass a block to iterate over each tuple without building the intermediate array:

names = ['Alice', 'Bob', 'Carol']
scores = [95, 87, 92]

names.zip(scores) do |name, score|
  puts "#{name}: #{score}"
end

# Output:
# Alice: 95
# Bob: 87
# Carol: 92
# => nil

When called with a block, zip returns nil.

Practical Examples

Merging keys and values into a hash

The most common use case — pairing keys with values:

keys   = [:name, :age, :city]
values = ['Alice', 30, 'Boston']

keys.zip(values).to_h
# => {:name => "Alice", :age => 30, :city => "Boston"}

Or equivalently, using Hash[]:

Hash[keys.zip(values)]
# => {:name => "Alice", :age => 30, :city => "Boston"}

Creating an indexed list

items  = ['apple', 'banana', 'cherry']
indexes = [0, 1, 2]

items.zip(indexes).to_h
# => {"apple" => 0, "banana" => 1, "cherry" => 2}

Pairing data for iteration

When you have parallel arrays and want to process them together:

prices  = [29.99, 49.99, 9.99]
names   = ['book', 'shirt', 'pen']

prices.zip(names).each do |price, name|
  puts "#{name} costs $#{price}"
end

# Output:
# book costs $29.99
# shirt costs $49.99
# pen costs $9.99

Interleaving two arrays

left  = [1, 3, 5]
right = [2, 4, 6]

left.zip(right).flatten
# => [1, 2, 3, 4, 5, 6]

zip vs transpose

Both zip and transpose rearrange elements into nested arrays, but they work on different data structures:

MethodOperates onWhat it does
zipMultiple separate arraysPairs elements at the same index
transposeA single 2D arraySwaps rows and columns
# zip pairs elements from separate arrays
[1, 2, 3].zip(['a', 'b', 'c'])
# => [[1, "a"], [2, "b"], [3, "c"]]

# transpose swaps rows and columns of a 2D array
matrix = [[1, 2], ['a', 'b'], [:x, :y]]
matrix.transpose
# => [[1, "a", :x], [2, "b", :y]]

Key difference: transpose requires a rectangular (consistent row length) structure and raises an error if rows differ. zip naturally handles unequal lengths by stopping at the shortest array.

Performance Notes

zip has O(n) time complexity where n is the length of the shortest array. It iterates once and creates a new array of n tuples.

Memory considerations

  • zip always allocates a new array
  • The inner tuples are new array objects
  • For large datasets, this allocation overhead adds up

When to use it

zip is ideal when you have parallel arrays that represent related data (keys/values, coordinates, name/score pairs). For quick iteration over parallel data, the block form avoids intermediate allocation:

# Block form: no intermediate array
[1, 2, 3].zip(['a', 'b', 'c']) { |n, l| puts "#{n}:#{l}" }

# No-block form: creates intermediate arrays
zipped = [1, 2, 3].zip(['a', 'b', 'c'])  # => [[1, "a"], [2, "b"], [3, "c"]]

Converting to hash efficiently

When you need a hash, .to_h on the result is cleaner than Hash[] and has equivalent performance:

keys.zip(values).to_h   # preferred in modern Ruby
Hash[keys.zip(values)]   # older style, same result

See Also