Arr::keyBy

Re-keys an array of records by one of their fields or by the return value of a callback.

Signature
public static function keyBy($array, $keyBy)
  • Added in Laravel 9.x

Parameters

ParameterTypeDescription
$$arrayiterableAn iterable of records, usually arrays or objects.
$$keyBycallable|array|stringA field name, resolved with dot notation, or a callback receiving the item and its key and returning the new key.

Return value

arrayAn array whose keys are the resolved values and whose values are the original items.

Arr::keyBy turns a list of records into a lookup table keyed by one of their fields. Internally it wraps the array in a collection and calls keyBy, so the field name is resolved with data_get — dot notation works and objects are traversed as well as arrays.

When to reach for it

Reach for it whenever you are about to write a loop that searches the same array repeatedly. Indexing rows by id once turns every later lookup into an O(1) array access, which is the standard fix for a nested loop that joins two result sets in PHP. It is also the tidy way to prepare data for a Blade view that renders by key.

What trips people up

Duplicate keys are not an error: the last record silently wins, so keying by a non-unique field loses rows. Count before and after if uniqueness matters, and group instead when it does not. The result is also no longer a list, so numeric ids produce an array that json_encode renders as an object. Records missing the field end up under the key "", all collapsing onto one another.

Example

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

When to use it

  • Indexing rows by id so a later join is a direct lookup instead of a nested loop.
  • Building a code-to-label map from an array of option records.
  • Preparing a keyed array for a view that renders items by identifier.

Frequently asked questions

What happens when two records share the same key in Arr::keyBy?

The later record silently replaces the earlier one, so the result can contain fewer entries than the input. Compare counts before and after if the field is meant to be unique.

Can Arr::keyBy use a nested field?

Yes, the field name is resolved with data_get, so dot notation such as 'meta.sku' works and object properties are traversed as well as array keys.

What is the difference between Arr::keyBy and array_column?

array_column can index by a top-level column only, while Arr::keyBy also accepts a callback and dot-notation paths, and keeps each item whole rather than extracting a single column.

More examples

Replacing a nested loop with a lookup

Prints "Grace spent 30" then "Ada spent 10". Building the index once makes each lookup a single array access, so the cost is linear instead of the quadratic cost of scanning $users inside the orders loop. The order of output follows $orders, not the index.

Example · php
<?php

use Illuminate\Support\Arr;

$users = [
    ['id' => 1, 'name' => 'Ada'],
    ['id' => 2, 'name' => 'Grace'],
];

$orders = [
    ['user_id' => 2, 'total' => 30],
    ['user_id' => 1, 'total' => 10],
];

$byId = Arr::keyBy($users, 'id');

foreach ($orders as $order) {
    echo $byId[$order['user_id']]['name'], ' spent ', $order['total'], "\n";
}

Detecting the silent overwrite

Prints "3 rows in, 2 out" then {"email":"[email protected]","role":"editor"}. The admin row was dropped because the second row reused the key. Comparing the two counts is the cheapest guard against keying by a field you assumed was unique.

Example · php
<?php

use Illuminate\Support\Arr;

$rows = [
    ['email' => '[email protected]', 'role' => 'admin'],
    ['email' => '[email protected]', 'role' => 'editor'],
    ['email' => '[email protected]', 'role' => 'viewer'],
];

$keyed = Arr::keyBy($rows, 'email');

echo count($rows), ' rows in, ', count($keyed), " out\n";
echo json_encode($keyed['[email protected]']), "\n";

Keying Eloquent results in an application

Each requested sku is reported with its product name, or 'not found' when the query returned no row for it, without re-querying inside the loop. This snippet needs a booted application for the Eloquent model; on a collection you would call keyBy('sku') directly instead of converting to an array first.

Example · php
<?php

use App\Models\Product;
use Illuminate\Support\Arr;

$products = Product::query()
    ->whereIn('sku', $skus)
    ->get()
    ->toArray();

$bySku = Arr::keyBy($products, 'sku');

foreach ($skus as $sku) {
    echo $sku, ': ', Arr::get($bySku, "$sku.name", 'not found'), "\n";
}

Discussion

  • Be the first to comment on this lesson.