Collection::mapWithKeys

Rebuilds a collection from key/value pairs returned by a callback for each item.

Signature
public function mapWithKeys(callable $callback): static

Parameters

ParameterTypeDescription
$$callbackcallableCalled as ($value, $key); must return an associative array containing a single key => value pair.

Return value

Illuminate\Support\CollectionA new collection built from the key/value pairs the callback returned.

Errors

The returned pair is iterated with foreach, so returning a scalar instead of an array raises a foreach warning and contributes nothing to the result.

Where map can only change values, mapWithKeys lets the callback decide the key as well. For each item the callback returns a one-element associative array, and those pairs are merged, in order, into the new collection.

When to reach for it

Building lookup tables and option lists: id => name for a select box, email => user for a deduplication pass, code => rate from an exchange-rate feed. Reach for keyBy instead when you want to keep the whole item as the value and only need to choose its key; use this method when the value changes too.

What trips people up

The callback must return an array. Returning the value directly is the single most common mistake and produces an empty or broken result rather than a clear error, because the framework simply iterates over what came back.

Duplicate keys collapse silently — the last pair written wins — so a non-unique key quietly shrinks the collection. And although the docs specify a single pair, the implementation merges every pair in the returned array, so returning several works but is not a documented contract.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Build an id => label array to feed a select box or a validation rule.
  • Convert a list of settings rows into a flat key => value config array.
  • Key a collection by one field while replacing the value with another.

Frequently asked questions

What must the callback return?

An associative array holding a single key => value pair, such as ['[email protected]' => 'Ada']. Returning a bare value instead of an array is the usual cause of an empty result.

How is it different from keyBy?

keyBy chooses a new key but keeps the whole original item as the value; this method lets you replace the value at the same time.

What happens if two items produce the same key?

The later pair overwrites the earlier one, so the resulting collection is shorter than the input with no warning.

More examples

Settings rows into a flat config array

The three rows collapse into a single associative array keyed by setting name, so lookups no longer need a search. Note that get() treats 'mail.port' as a literal key here, not as dot notation.

Example · php
<?php

$rows = collect([
    ['key' => 'mail.driver', 'value' => 'smtp'],
    ['key' => 'mail.port', 'value' => 587],
    ['key' => 'app.locale', 'value' => 'en'],
]);

$settings = $rows->mapWithKeys(fn (array $row) => [$row['key'] => $row['value']]);

print_r($settings->all());
echo $settings->get('mail.port') . PHP_EOL;

Duplicate keys collapse

Three rows become two entries: 'ada' keeps only the last date, 2024-01-03. That is useful when the input is sorted oldest-first and you want the newest per user, and a silent data loss when it is not.

Example · php
<?php

$logins = collect([
    ['user' => 'ada', 'at' => '2024-01-01'],
    ['user' => 'linus', 'at' => '2024-01-02'],
    ['user' => 'ada', 'at' => '2024-01-03'],
]);

$latest = $logins->mapWithKeys(fn (array $row) => [$row['user'] => $row['at']]);

print_r($latest->all());
echo 'in: ' . $logins->count() . ' out: ' . $latest->count() . PHP_EOL;

Options for a form

The idiomatic way to turn models into a select box's value => label array. It needs a booted application and a database, so it is shown rather than executed here.

Example · php
<?php

$options = Country::query()
    ->orderBy('name')
    ->get()
    ->mapWithKeys(fn (Country $country) => [$country->iso => $country->name])
    ->all();

return view('address.form', ['countries' => $options]);

Discussion

  • Be the first to comment on this lesson.