Copied from wiki.php.net, last synchronized Oct 25, 2026 14:46 UTC
Splitting an array into the elements that satisfy a condition and the elements that do not is a common operation. PHP has no built-in function for it, so code today does one of the following:
// Two passes, with the condition duplicated and negated
$adults = array_filter($users, fn($u) => $u->age >= 18);
$minors = array_filter($users, fn($u) => $u->age < 18);
// One filter plus a set difference on the keys
$adults = array_filter($users, fn($u) => $u->age >= 18);
$minors = array_diff_key($users, $adults);
// A hand-written loop
$adults = [];
$minors = [];
foreach ($users as $key => $user) {
if ($user->age >= 18) {
$adults[$key] = $user;
} else {
$minors[$key] = $user;
}
}
The first form repeats the condition and calls the callback twice per element. The second relies on a less obvious idiom. The third is correct but verbose for a simple intent.
This RFC proposes a single function that states the intent directly:
[$adults, $minors] = array_partition($users, fn($u) => $u->age >= 18);
Add the following function to ext/standard:
function array_partition(array $array, callable $callback): array {}
The callback is called once for each element, in array order, and receives the value and the key, like array_find(), array_find_key(), array_any() and array_all(). The function returns a list of exactly two arrays:
0: the elements for which the callback returned a truthy value;1: all other elements.Keys are preserved in both arrays, and the relative order of the elements is kept. The input array is not modified.
var_dump(array_partition([1, 2, 3, 4, 5], fn($v) => $v % 2 === 1));
// [[0 => 1, 2 => 3, 4 => 5], [1 => 2, 3 => 4]]
[$named, $indexed] = array_partition(
['a' => 1, 'b' => 2, 5 => 3, 6 => 4],
fn($value, $key) => is_string($key),
);
// $named = ['a' => 1, 'b' => 2]
// $indexed = [5 => 3, 6 => 4]
var_dump(array_partition([], fn($v) => true));
// [[], []]
array_filter().array_partition($a, $cb)[0] is identical (===) to array_filter($a, $cb, ARRAY_FILTER_USE_BOTH), and array_partition($a, $cb)[1] is identical to array_diff_key($a, array_partition($a, $cb)[0]).], [, so the result can always be destructured into two variables.array_filter().$mode parameter. The callback always receives both the value and the key, following array_find() rather than array_filter(). A callback that accepts only the value works unchanged, because userland functions ignore extra arguments.Like array_find(), passing an internal function with a fixed arity as the callback fails, because it receives two arguments:
array_partition(['x'], 'is_string');
// ArgumentCountError: is_string() expects exactly 1 argument, 2 given
array_partition(['x'], is_string(...)); // same error
array_partition(['x'], fn($v) => is_string($v)); // works
Every prior-art implementation listed below returns the matching elements first. That order also reads naturally in the common destructuring form [$matching, $rest] = array_partition(...).
A result keyed by true and false was suggested during the discussion. PHP casts boolean array keys to integers (true becomes 1 and false becomes 0), so such a result would have to put the non-matching elements at index 0. Whichever layout is chosen, one of the two idioms silently returns the wrong bucket. This RFC keeps the order used by other languages. Note that, with the proposed layout, the following does not work as it reads:
// Wrong: true is cast to 1, so $passed receives the NON-matching elements
[true => $passed, false => $rejected] = array_partition($values, $callback);
// Correct
[$passed, $rejected] = array_partition($values, $callback);
A function that groups elements by an arbitrary key returned by the callback is more general, and is already the subject of a separate RFC (see “Related RFCs”). It does not replace a two-way split:
$groups[true] ?? []) for each side.partition and groupBy, Rust partition and the itertools group_by, Laravel partition and groupBy).The two proposals are complementary, and this RFC does not block a future grouping function.
The parameters are named $array and $callback, as for the other callback-taking array functions (array_filter(), array_map(), array_walk(), array_find(), array_find_key(), array_any(), array_all()). $predicate was suggested as a more descriptive name. It was not chosen, so that the callback: named argument works the same way across the whole family.
Illuminate\Support\Collection::partition() returns two collections, passing first. The callback receives the value and the key, and keys are preserved.Enumerable#partition returns [true_elements, false_elements].Iterable<T>.partition() returns a Pair whose first holds the elements for which the predicate is true.Iterator::partition() returns a tuple (matching, non_matching).Data.List.partition returns a pair (matching, non_matching).Array.prototype.partition proposal was published in 2020 (https://github.com/joaolucasl/proposal-array-prototype-partition, discussion at https://es.discourse.group/t/array-prototype-partition-like-filter-but-two-arrays-for-true-and-false/447) but did not advance. ECMAScript 2024 added the general Object.groupBy() and Map.groupBy() instead. Lodash provides _.partition().The function name array_partition is taken in the global namespace. Code that declares its own global array_partition() without a function_exists() guard fails with “Cannot redeclare function array_partition()” and must rename or namespace it.
Existing userland functions with this name often use a different meaning: they split an array into N chunks.
array-partition WordPress plugin by coffee2code defined a global array_partition($array, $number_of_columns) in version 1.0, guarded by function_exists() from 1.0.2. It was renamed to c2c_array_partition() in 1.1 and the deprecated name was removed in 1.3 (2020-09-10). Sites running a version older than 1.3 would no longer fatal on declaration because of the guard. However, calls such as array_partition($posts, 3) would reach the native function and throw a TypeError.array_partition() that splits an array into a given number of parts, like array_chunk() but by count. It was closed by its author, who noted that array_chunk($array, (int) ceil(count($array) / $n)) covers the use case.The name was kept because “partition” by predicate is the established meaning across the languages listed under “Prior art”, and because the N-chunks operation is already available through array_chunk(). The documentation should state the difference explicitly.
Next minor version (PHP 8.7).
IDEs, language servers and static analysers need to add the new function to their stubs. A precise return type can be expressed as array{0: array<K, V>, 1: array<K, V>} for an input of type array<K, V>. Polyfills are straightforward, because the behaviour can be reproduced with array_filter() and array_diff_key(), or with a foreach loop.
None. Only ext/standard gains a function. array_filter() and all other existing array functions are unchanged.
None.
The value of this function is clarity, not speed: the cost is dominated by invoking the callback. Measured on a release build (./configure --disable-all, php -n), Apple M3 Pro under Docker (aarch64), callback fn($v) => $v % 2 === 0, median of 21 runs over 3 rounds, 1,000,000 elements:
| Approach | Packed int list | String keys |
|---|---|---|
array_partition() |
40.2 - 42.2 ms | 42.1 - 45.3 ms |
array_filter() + array_diff_key() |
39.8 - 40.4 ms | 42.8 - 44.6 ms |
array_filter() twice |
88.5 ms | 90.3 ms |
foreach loop |
34.8 - 35.7 ms | 40.4 - 45.4 ms |
array_partition() is on par with array_filter() + array_diff_key() (within about 2%), about 2.2 times faster than filtering twice, and about 5 - 12% slower than a hand-written loop, which avoids the function call per element. The benchmark script and the raw output are available on request.
None at this time.
iterable or generators (for example accepting a Traversable and returning two arrays). This RFC follows the other array_* functions and only accepts arrays.Primary Vote requiring a 2/3 majority to accept the RFC:
Vote: Add array_partition() as outlined in the RFC? — Yes: 0 · No: 0 · Abstain: 0 (closed)
https://github.com/php/php-src/pull/24568
After the RFC is implemented, this section should contain:
$mode parameter mirroring array_filter() (ARRAY_FILTER_USE_KEY / ARRAY_FILTER_USE_BOTH): removed after review in favour of the array_find() signature.true / false: see “Why the matching elements come first”.$predicate: see “Parameter naming”.