Arr::where

Filters an array with a callback that receives both the value and the key.

Signature
public static function where($array, callable $callback)

Parameters

ParameterTypeDescription
$$arrayarrayThe array to filter.
$$callbackcallableReceives ($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

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

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.

Example · php
<?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.

Example · php
<?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

  • Be the first to comment on this lesson.