Collection::zip
Merges the collection with one or more iterables by position, pairing item 0 with item 0.
public function zip(Arrayable|iterable ...$items): static- Added in Laravel 5.1
Parameters
| Parameter | Type | Description |
|---|---|---|
$...$items | \Illuminate\Contracts\Support\Arrayable|iterable | One or more iterables to merge with the collection by index. Variadic: pass two arrays to build triples, three to build quadruples, and so on. |
Return value
staticA new zero-indexed collection whose items are themselves collections, each holding one value from every input at that position. Its length is that of the longest input.
Errors
Throws ArgumentCountError when called with no arguments — at least one iterable is required.
Changelog
- PHP 5.1Added. Implemented on top of array_map with multiple arrays, which is why shorter inputs are padded with null rather than truncating the result.
zip stitches the collection together with one or more iterables by position: index 0 of every input becomes the first tuple, index 1 the second, and so on. It is the collection equivalent of Python's zip, or a positional join across parallel arrays.
When to reach for it
Pairing a CSV header row with a data row, joining labels to values that came back from a separate call, or turning several parallel arrays into records you can map over. It takes any number of arguments, so $names->zip($prices, $stock) produces triples.
What trips people up
- Each tuple is a Collection, not an array. The docs print the result as
[['Chair', 100]], butis_array()on a tuple isfalse, andarray_maporlist()over the tuples will not behave as you expect. CalltoArray()on the outer collection to get plain nested arrays; bracket access still works because Collection implements ArrayAccess. - The longest input wins. Built on
array_mapwith several arrays, so short inputs are padded withnull.zipnever truncates to the shortest input. - Keys are discarded. Both the collection's keys and the arguments' keys are dropped; the result is always a zero-indexed list.
If you wanted keys instead of positional tuples, combine is the method you are actually after.
Example
When to use it
- Pair a CSV header row with each data row before turning them into records.
- Join a list of product names to a separately fetched list of prices and stock counts.
- Line up two API responses that are guaranteed to come back in the same order.
- Build coordinate pairs from parallel x and y arrays.
Frequently asked questions
What happens when the arrays passed to zip have different lengths?
The result is as long as the longest input and every shorter input is padded with null; zip never truncates to the shortest. This falls out of array_map's behaviour with multiple arrays.
Why is each zipped pair a Collection instead of an array?
zip builds each tuple with new static(func_get_args()), so the return type is a collection of collections even though the docs print it as nested arrays. Call toArray() on the outer collection to get plain arrays.
Can Collection::zip take more than one array?
Yes, it is variadic: $names->zip($prices, $stock) produces three-element tuples, and each extra argument adds another element to every tuple.
More examples
Three parallel arrays into records
zip is variadic, so two arguments produce triples. Inside map, $tuple is a Collection and $tuple[0] reads through ArrayAccess. If you prefer named arguments over indexes, swap map for mapSpread, which unpacks each tuple into separate callback parameters.
<?php
$names = collect(['Chair', 'Desk']);
$prices = [100, 200];
$stock = [3, 0];
$rows = $names->zip($prices, $stock)->map(fn ($tuple) => [
'name' => $tuple[0],
'price' => $tuple[1],
'in_stock' => $tuple[2] > 0,
]);
echo json_encode($rows->all()), "\n";
// [{"name":"Chair","price":100,"in_stock":true},
// {"name":"Desk","price":200,"in_stock":false}]zip versus combine
Both walk the two lists in lockstep, but the shapes differ completely. Reaching for zip when you wanted a keyed record is the most common mistake with this method — if the first collection holds names for the second one's values, use combine.
<?php
$headers = collect(['sku', 'name', 'price']);
$row = ['DK-01', 'Desk', 199];
// zip pairs positionally: one tuple per column.
echo json_encode($headers->zip($row)->toArray()), "\n";
// [["sku","DK-01"],["name","Desk"],["price",199]]
// combine uses the collection as keys: one keyed record.
echo json_encode($headers->combine($row)->all()), "\n";
// {"sku":"DK-01","name":"Desk","price":199}Unequal lengths and discarded keys
The second line shows the padding running in the other direction: it is the collection itself that gets the nulls when an argument is longer. Because zip never truncates, a length mismatch produces silent nulls rather than an error, so slice the inputs yourself when a ragged result would be a bug.
<?php
// String keys on either side are thrown away.
$labelled = collect(['a' => 'Chair', 'b' => 'Desk']);
echo json_encode($labelled->zip(['x' => 100, 'y' => 200])->toArray()), "\n";
// [["Chair",100],["Desk",200]]
// The result is as long as the LONGEST input, padded with null.
echo json_encode(collect(['Chair'])->zip([100, 200, 300])->toArray()), "\n";
// [["Chair",100],[null,200],[null,300]]
// Guard against ragged inputs by trimming first.
$a = collect(['Chair']);
$b = [100, 200, 300];
echo json_encode($a->zip(array_slice($b, 0, $a->count()))->toArray()), "\n";
// [["Chair",100]]
Discussion