Использование array_udiff_uassoc в PHP
Узнайте, как использовать PHP-функцию array_udiff_uassoc для вычисления разности двух или более массивов с пользовательскими функциями сравнения.
array_udiff_uassoc() вычисляет разность двух или более массивов, проверяя и ключи, и значения — при этом вы сами определяете, как сравниваются и те, и другие, передавая два собственных колбэка. Элемент из первого массива попадает в результат только в том случае, если ни один другой массив не содержит элемента, совпадающего с ним одновременно по ключу и значению, согласно вашим колбэкам.
На этой странице подробно описывается, как взаимодействуют два колбэка (это то, что большинство справочников обходит стороной), приводится работающий пример и объясняется, когда эта функция является правильным инструментом по сравнению с более простыми аналогами.
Что такое array_udiff_uassoc?
array_udiff_uassoc() возвращает записи из $array1, которые отсутствуют в любом из других массивов. В отличие от array_diff(), которая сравнивает только значения через приведение к строке, этот вариант проверяет ключи и значения вместе и делегирует оба сравнения пользовательским функциям — именно это означают два символа u в названии (udiff = пользовательское сравнение значений, uassoc = пользовательское сравнение ключей).
Сигнатура функции:
Синтаксис функции PHP array_udiff_uassoc
array_udiff_uassoc(
array $array1,
array $array2,
array ...$arrays, // one or more additional arrays
callable $value_compare_func,
callable $key_compare_func
): arrayДва колбэка всегда являются последними двумя аргументами в следующем порядке: сначала сравнение значений, затем сравнение ключей. Всё, что стоит перед ними, является массивом. Результат — новый массив записей из $array1, прошедших сравнение, с сохранением их исходных ключей.
Как на самом деле происходит сопоставление. Для каждой записи из
$array1PHP перебирает остальные массивы в поисках записи, ключ которой является «равным» согласно$key_compare_funcи значение которой является «равным» согласно$value_compare_func. Если такое совпадение найдено в любом другом массиве, запись исключается; иначе — сохраняется. Запись удаляется только тогда, когда оба сравнения фиксируют равенство.
Понимание функций сравнения
Каждый колбэк принимает два аргумента и должен возвращать целое число, точно так же, как компаратор сортировки:
- Вернуть
0, если два элемента считаются равными. - Вернуть положительное целое, если первый «больше».
- Вернуть отрицательное целое, если первый «меньше».
PHP интересует только то, равно ли возвращаемое значение 0 (равны) или нет, однако требуется последовательный трёхпутевой результат, так как функция выполняет внутреннюю сортировку. Оператор «космический корабль» <=> — самый удобный способ его написать:
$value_compare_func = fn($a, $b) => $a <=> $b; // strict ordering
$key_compare_func = fn($a, $b) => strcasecmp((string) $a, (string) $b); // case-insensitive keysПоскольку вы определяете оба сравнения, можно делать то, что встроенные функции разности не умеют — например, сравнивать ключи без учёта регистра или сравнивать объекты по одному свойству.
Пример: базовое использование
<?php
function compare_values($a, $b) {
if ($a === $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
}
function compare_keys($a, $b) {
if ($a === $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
}
$array1 = array('a' => 'apple', 'b' => 'banana', 'c' => 'cherry', 'd' => 'durian');
$array2 = array('a' => 'apple', 'b' => 'game', 'c' => 'cherry');
$array3 = array('a' => 'apple', 'b' => 'door', 'c' => 'cherry', 'g' => 'durian');
$result = array_udiff_uassoc($array1, $array2, $array3, 'compare_values', 'compare_keys');
print_r($result);
?>Здесь compare_values и compare_keys — обычные трёхпутевые компараторы. Вызов вычисляет разность $array1 относительно $array2 и $array3, оставляя только те записи, чей ключ и значение нигде не совпадают. Результат:
Array
(
[b] => banana
[d] => durian
)Разберём, почему каждая запись сохраняется или исключается:
a => apple— исключена: в$array2(и в$array3) есть тот же ключaи то же значениеapple.b => banana— сохранена: в остальных массивах ключbприсутствует, но их значения —game/door, неbanana. Значения различаются, поэтому совпадения нет.c => cherry— исключена: совпадает в обоих других массивах.d => durian— сохранена: в$array3есть значениеdurian, но под ключомg, а неd. Ключи различаются, поэтому совпадения нет.
Последний случай — самая суть функции: даже если значение durian встречается в другом массиве, ключ не совпадает, и запись остаётся. Функция сравнения только по значению, например array_udiff(), удалила бы её.
Пример: ключи без учёта регистра
Поскольку сравнение ключей вы определяете сами, можно игнорировать регистр ключей при строгом сравнении значений:
<?php
$wanted = ['x' => 10, 'y' => 20, 'z' => 30];
$current = ['x' => 10, 'Y' => 20, 'z' => 99];
$result = array_udiff_uassoc(
$wanted,
$current,
fn($v1, $v2) => $v1 <=> $v2, // values: strict ordering
fn($k1, $k2) => strcasecmp((string) $k1, (string) $k2) // keys: case-insensitive
);
print_r($result);Результат:
Array
(
[z] => 30
)x => 10 и y => 20 удалены (в current есть то же значение под ключом, совпадающим без учёта регистра), тогда как z => 30 сохранена, потому что в current стоит z => 99 — ключ совпадает, но значение 99 !== 30.
Когда использовать (и чем заменить)
Используйте array_udiff_uassoc() только тогда, когда вам нужна пользовательская логика для обоих — и ключей, и значений. Если требования скромнее, более простой аналог будет быстрее читаться и писаться:
| Что нужно контролировать… | Используйте |
|---|---|
| Только значения (колбэк), ключи игнорируются | array_udiff() |
Значения (колбэк) + ключи через === | array_udiff_assoc() |
Ключи (колбэк) + значения через === | array_diff_uassoc() |
| Ничего — простая разность по значениям | array_diff() |
Распространённые ошибки
- Порядок аргументов. Колбэки — два последних аргумента, сначала сравнение значений. Передача в неправильном порядке молча даёт неверные результаты без каких-либо ошибок.
- Не менее двух массивов. Необходимо передать
$array1, хотя бы один другой массив и затем оба колбэка — минимум пять аргументов. - Возвращайте
int, а неbool. Возвратtrue/falseиз компаратора работает случайно (они приводятся к1/0), но нарушает упорядочивание. Используйте<=>или явные-1/0/1. - Ключи сохраняются. В результате сохраняются исходные ключи из
$array1; переиндексация не производится.
Если колбэки для вас новы, смотрите PHP Callback Functions, а для общего повторения массивов — PHP Arrays.
Заключение
array_udiff_uassoc() — наиболее гибкая из функций разности массивов в PHP: она сравнивает записи и по ключу, и по значению, передавая оба сравнения вашим собственным колбэкам. Запись из первого массива сохраняется только тогда, когда ни один другой массив не совпадает с ней по обоим критериям. Используйте её, когда встроенных правил сравнения (===, приведение к строке) недостаточно — например, для ключей без учёта регистра, значений с учётом локали или сравнения объектов по полю — и переходите к более простому варианту array_diff*, когда такая гибкость не нужна.