Collection::keyBy

Re-keys a collection by a field or callback so items can be looked up by that value.

Signature
public function keyBy(callable|array|string $keyBy): static

Parameters

ParameterTypeDescription
$$keyBycallable|array|stringThe field to key by (dot notation supported), or a callback receiving ($item, $key) that returns the key to use.

Return value

Illuminate\Support\CollectionA new collection whose keys are the resolved values and whose values are the original items.

keyBy turns a numerically indexed list of rows into a lookup table. It walks the collection, resolves a key for every item, and returns a new collection where that key points at the whole original item. The values are untouched — only the keys change.

When to reach for it

Any time you are about to write a loop that searches a collection over and over. Keying once and then reading $byId[$id] turns repeated O(n) scans into direct lookups. It is also the standard way to line two datasets up before comparing them: key both by the same identifier, then iterate one and index into the other.

What trips people up

The key is resolved with data_get, so 'meta.code' reaches into nested arrays and objects rather than looking for a literal key containing a dot. Resolved keys that are enums are converted with enum_value, and other objects are cast to string — so an object without __toString will error rather than silently work.

The big one is collisions. There is no warning and no exception when two items resolve to the same key: the later item simply replaces the earlier one, and the output collection is quietly shorter than the input. Compare count() before and after if uniqueness matters.

Example

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

When to use it

  • Build an id => model lookup so a loop over another dataset can resolve relations without querying again.
  • Index a config or CSV import by its natural key before diffing it against existing records.
  • Key rows by a composite value (region plus month) to join two report datasets in memory.

Frequently asked questions

What happens when two items have the same key?

The last one wins and the earlier item is discarded, with no error. The resulting collection is smaller than the original, so use groupBy instead when you need to keep every row that shares a key.

Can I key by a nested value?

Yes — pass a dot-notation string such as 'venue.code', because the key is resolved with data_get. A callback works too and is clearer when the key has to be built from more than one field.

Does it change the original collection?

No. A new collection instance is returned and the original keeps its keys, like almost every other collection method.

More examples

Keying by a callback

The callback builds a composite key from two fields, so rows that are not unique on any single column still get a stable, unique key. The printed key list is ['eu-01', 'us-01', 'eu-02'] and the lookup returns 140.

Example · php
<?php

$shifts = collect([
    ['region' => 'eu', 'month' => '01', 'hours' => 120],
    ['region' => 'us', 'month' => '01', 'hours' => 90],
    ['region' => 'eu', 'month' => '02', 'hours' => 140],
]);

$keyed = $shifts->keyBy(fn (array $row) => $row['region'] . '-' . $row['month']);

print_r($keyed->keys()->all());
echo $keyed->get('eu-02')['hours'] . PHP_EOL;

Dot notation into nested data

Because the key is resolved with data_get, 'venue.code' descends into the nested array. The result is keyed 'DAL' and 'YYZ'. If you actually have a top-level array key literally named "venue.code", pass a callback instead — the string form will never find it.

Example · php
<?php

$events = collect([
    ['name' => 'Laracon', 'venue' => ['code' => 'DAL']],
    ['name' => 'VueConf', 'venue' => ['code' => 'YYZ']],
]);

print_r($events->keyBy('venue.code')->all());

Joining two datasets in memory

Keying the totals once makes each lookup constant time instead of a nested search. The output shows Ada with 125 and Linus with 400, and any user missing from the totals falls back to 0.

Example · php
<?php

$users = collect([
    ['id' => 1, 'name' => 'Ada'],
    ['id' => 2, 'name' => 'Linus'],
]);

$totals = collect([
    ['user_id' => 2, 'spent' => 400],
    ['user_id' => 1, 'spent' => 125],
])->keyBy('user_id');

$joined = $users->map(fn (array $u) => $u + [
    'spent' => $totals->get($u['id'])['spent'] ?? 0,
]);

print_r($joined->all());

Discussion

  • Be the first to comment on this lesson.