Signature
array_diff_uassoc(array $array, array ...$arrays, callable $key_compare_func): arrayPurpose
array_diff_uassoc() returns the entries of the first array that do not appear in any of the other arrays. An entry counts as "appearing" only when both parts match:
- the values are equal when compared as strings, and
- the keys are considered equal by a callback you supply.
That makes it a middle ground between array_diff_assoc(), which uses PHP's built-in strict key comparison, and array_udiff_uassoc(), which lets you customize both keys and values. Reach for it when keys need fuzzy matching, such as case-insensitive field names or keys with a prefix.
Signature
Parameters
$array: the array to compare from. Only its entries can appear in the result....$arrays: one or more arrays to compare against. An entry is removed if a match exists in any of them.$key_compare_func: the last argument, with no default. It receives two keys and must return an integer that is less than, equal to, or greater than zero. Returning0means the keys are equal.
The callback has the shape callback(mixed $a, mixed $b): int. Keys may be integers or strings, so write a comparator that copes with both. Do not rely on which array each argument came from. Make the comparison symmetric.
Return value and failures
The function returns a new array containing the entries of $array with no match elsewhere. Keys are preserved and nothing is reindexed. If every entry has a match, the result is an empty array.
Passing a non-array where an array is expected, or a callback PHP cannot call, raises a TypeError. Because the callback is the final argument, calling the function with only one array is also an error.
Example 1: case-insensitive keys
Using the built-in strcasecmp as the key comparator, Name and name count as the same key. Only entries whose value also matches are removed.
$expected = ['Name' => 'Ada', 'EMAIL' => 'ada@example.com', 'role' => 'admin'];
$actual = ['name' => 'Ada', 'email' => 'ada@example.org', 'ROLE' => 'admin'];
$diff = array_diff_uassoc($expected, $actual, 'strcasecmp');
echo json_encode($diff, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"EMAIL":"ada@example.com"}
The Name and role entries match on both key and value, so they disappear. The EMAIL entry has a matching key but a different value, so it stays.
Example 2: several arrays and string value comparison
Values are compared as strings, so integer 1 equals string '1'. With multiple comparison arrays, a match in any one of them removes the entry.
$base = ['x' => 1, 'y' => 2, 'z' => 3];
$first = ['x' => '1', 'y' => 3];
$second = ['z' => 3, 'q' => 2];
$diff = array_diff_uassoc(
$base,
$first,
$second,
fn(string|int $a, string|int $b): int => $a <=> $b
);
echo json_encode($diff, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"y":2}
Key x matches $first because 1 and '1' are equal as strings. Key z matches $second. Key y holds 2, but $first has 3 under y and $second has no y, so it is the only survivor. The value 2 under q in $second does not help, because the keys differ.
Example 3: ignoring a key prefix
The comparator can normalize keys before comparing them. Here item_1 is treated as the same key as 1.
$old = ['item_1' => 'pen', 'item_2' => 'ink', 'item_3' => 'pad'];
$new = [1 => 'pen', 2 => 'cup', 3 => 'pad'];
$strip = fn(string|int $key): string => str_replace('item_', '', (string) $key);
$diff = array_diff_uassoc(
$old,
$new,
fn(string|int $a, string|int $b): int => strcmp($strip($a), $strip($b))
);
echo json_encode($diff, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"item_2":"ink"}
The integer keys in $new are converted to strings inside the callback, so both sides normalize cleanly. Only item_2 remains, because its value ink differs from cup.
Edge cases
- Key order and keys are kept. The result keeps the original keys and order of
$array, so it can be a non-list array. - Only one level is compared. Nested arrays are not diffed deeply. To compare a deeper level, call the function on the nested arrays yourself.
- Values are strings. Because values are compared by their string form, nested arrays or objects without a string conversion are poor candidates as values.
- Auto-assigned keys matter. Lists such as
['red']have key0, so the same value at a different position may not match. - Float returns are truncated. If the callback returns a float, it is cast to an integer, so
0.5becomes0and counts as equal.
Common mistakes
- Returning a boolean from the callback. A comparator such as
fn($a, $b) => $a === $breturnstruefor equal keys, which becomes1, andfalsefor different keys, which becomes0, meaning equal. The logic is inverted. Return0for equal keys. - Expecting the callback to compare values. It only compares keys. For value comparison with a callback, look at
array_udiff_assoc()orarray_udiff_uassoc(). - Writing an asymmetric comparator. The order of the two arguments is not something to depend on, so compare in a way that gives consistent results either way.
- Forgetting that the first array is the only source. Entries that exist only in the later arrays are never returned. For a two-way difference, run the function in both directions.
- Expecting reindexed output. If you need a list, wrap the result in
array_values().
Related functions
array_diff()compares values only.array_diff_assoc()compares values and keys using built-in key comparison.array_udiff()uses a callback for values.array_udiff_assoc()uses a callback for values and built-in comparison for keys.array_udiff_uassoc()uses callbacks for both values and keys.array_intersect_assoc()keeps matches instead of removing them.
Recap
array_diff_uassoc() keeps entries of the first array that have no match elsewhere, where a match means equal string values and keys that your callback reports as equal with 0. Keys are preserved, only one dimension is checked, and the callback must return an integer rather than a boolean.
Tested examples
$expected = ['Name' => 'Ada', 'EMAIL' => 'ada@example.com', 'role' => 'admin'];
$actual = ['name' => 'Ada', 'email' => 'ada@example.org', 'ROLE' => 'admin'];
$diff = array_diff_uassoc($expected, $actual, 'strcasecmp');
echo json_encode($diff, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"EMAIL":"ada@example.com"}
$base = ['x' => 1, 'y' => 2, 'z' => 3];
$first = ['x' => '1', 'y' => 3];
$second = ['z' => 3, 'q' => 2];
$diff = array_diff_uassoc(
$base,
$first,
$second,
fn(string|int $a, string|int $b): int => $a <=> $b
);
echo json_encode($diff, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"y":2}
$old = ['item_1' => 'pen', 'item_2' => 'ink', 'item_3' => 'pad'];
$new = [1 => 'pen', 2 => 'cup', 3 => 'pad'];
$strip = fn(string|int $key): string => str_replace('item_', '', (string) $key);
$diff = array_diff_uassoc(
$old,
$new,
fn(string|int $a, string|int $b): int => strcmp($strip($a), $strip($b))
);
echo json_encode($diff, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"item_2":"ink"}