Arr::where
Filters an array with a callback that receives both the value and the key.
public static function where($array, callable $callback)Parameters
| Parameter | Type | Description |
|---|---|---|
$$array | array | The array to filter. |
$$callback | callable | Receives ($value, $key) and returns true to keep the entry. |
Return value
arrayA new array containing the entries whose callback returned true, with the original keys preserved.
Arr::where is a thin wrapper over array_filter($array, $callback, ARRAY_FILTER_USE_BOTH). Its whole reason to exist is that the flag is easy to forget and the argument order is the reverse of what people expect.
When to reach for it
- Filtering when the decision depends on the key as well as the value — dropping keys with a given prefix, or keeping only whitelisted keys with a value test.
- Removing empty, invalid or placeholder entries from a payload before it is stored or sent.
- Splitting a config array into the parts different subsystems handle.
What trips people up
The callback receives ($value, $key), in that order. array_filter's ARRAY_FILTER_USE_KEY mode passes the key first, so code moved between the two silently inverts.
Keys are preserved and the result becomes gappy. Filtering [100, '200', 300, '400', 500] gives keys 1 and 3. This matters at three boundaries: json_encode emits a JSON object instead of an array, $result[0] may not exist, and array_merge versus the + operator behave differently. Call array_values() when you need a list back.
The return value is truthiness-based. Returning 0, '' or null from the callback drops the entry; a callback with a missing return on some branch filters everything on that path.
The counterpart Arr::whereNot keeps the entries that fail the same test.
Example
When to use it
- Drop config entries whose key starts with an internal prefix.
- Strip empty or placeholder values out of a payload before persisting it.
- Keep only the array entries whose value passes a validation check.
Frequently asked questions
What order does the Arr::where callback receive its arguments in?
Value first, key second, matching array_filter's ARRAY_FILTER_USE_BOTH mode. This is the opposite of ARRAY_FILTER_USE_KEY, which passes only the key.
Why does json_encode turn my filtered array into an object?
Because Arr::where preserves the original keys, leaving gaps that stop the array being a list. Wrap the result in array_values() before encoding.
How do I keep the items that fail the test instead?
Use Arr::whereNot with the same callback — it filters using the negation, so the two calls together partition the array.
More examples
Argument order versus array_filter
All three produce the same result here, but only USE_BOTH gives the callback both arguments in the same order as Arr::where. Copying a USE_KEY callback into Arr::where without swapping the parameters is the usual source of an inverted filter.
<?php
use Illuminate\Support\Arr;
$data = ['id' => 1, 'name' => 'Desk', '_token' => 'abc', '_method' => 'PUT'];
// Arr::where: ($value, $key)
$viaArr = Arr::where($data, fn ($value, $key) => ! str_starts_with($key, '_'));
// array_filter with USE_KEY: only the key, and no value available
$viaKeyMode = array_filter($data, fn ($key) => ! str_starts_with($key, '_'), ARRAY_FILTER_USE_KEY);
// array_filter with USE_BOTH: ($value, $key) — identical to Arr::where
$viaBoth = array_filter($data, fn ($value, $key) => ! str_starts_with($key, '_'), ARRAY_FILTER_USE_BOTH);
var_dump($viaArr === $viaKeyMode, $viaArr === $viaBoth);
print_r($viaArr);
/*
bool(true)
bool(true)
Array
(
[id] => 1
[name] => Desk
)
*/Filtering on value and key together
One pass makes a decision that needs both halves of the entry — a value test and a key prefix test — which is exactly the case plain array_filter cannot express without a flag. The two outputs together account for all five original entries.
<?php
use Illuminate\Support\Arr;
$limits = [
'api.rate' => 60,
'api.burst' => 0,
'ui.timeout' => 30,
'ui.retries' => 0,
'internal.debug' => 5,
];
// Keep public settings that are actually enabled.
$public = Arr::where(
$limits,
fn (int $value, string $key) => $value > 0 && ! str_starts_with($key, 'internal.')
);
print_r($public);
// Arr::whereNot keeps everything the same test rejects.
print_r(Arr::whereNot(
$limits,
fn (int $value, string $key) => $value > 0 && ! str_starts_with($key, 'internal.')
));
/*
Array
(
[api.rate] => 60
[ui.timeout] => 30
)
Array
(
[api.burst] => 0
[ui.retries] => 0
[internal.debug] => 5
)
*/
Discussion