在 PHP 中,array_diff() 是一个非常常用的函数,它用于比较两个或多个数组,并返回出现在第一个数组中但不在其他数组中的值。这对于一维数组来说非常好用,但当你试图用它来处理“嵌套数组”(即数组中的元素仍然是数组)时,就会发现事情没有你想象中那么简单。
array_diff() 的官方定义是比较数组的“值”,并返回第一个数组中存在、而其他数组中不存在的那些“值”。举个简单的一维数组例子:
$a = ['apple', 'banana', 'cherry'];
$b = ['banana', 'dragonfruit'];
$result = array_diff($a, $b);
// 结果是 ['apple', 'cherry']
这非常直接——只比较值,不管键。然而,如果我们改用嵌套数组,结果就不同了。
来看下面的例子:
$a = [
['id' => 1, 'name' => 'Alice'],
['id' => 2, 'name' => 'Bob']
];
$b = [
['id' => 2, 'name' => 'Bob']
];
$result = array_diff($a, $b);
你可能期望 $result 会只返回 ['id' => 1, 'name' => 'Alice'],但实际上你会得到整个 $a 都被返回。这是因为 PHP 在比较数组时,内部使用 == 来比较值,而嵌套数组的比较方式并不像你想象得那么“智能”。
array_diff() 在处理数组里的数组时,其内部机制不会递归处理,而是将整个子数组视为一个“值”,并尝试用 string 类型进行比较。而子数组一旦结构稍微不一致(比如键顺序不同),就会被视为不同的值,哪怕内容完全一样。
为了正确比较嵌套数组,我们可以写一个自定义函数,逐项比较数组内容:
function array_diff_recursive($a, $b) {
$diff = [];
foreach ($a as $itemA) {
$found = false;
foreach ($b as $itemB) {
if ($itemA == $itemB) {
$found = true;
break;
}
}
if (!$found) {
$diff[] = $itemA;
}
}
return $diff;
}
$a = [
['id' => 1, 'name' => 'Alice'],
['id' => 2, 'name' => 'Bob']
];
$b = [
['id' => 2, 'name' => 'Bob']
];
$result = array_diff_recursive($a, $b);
// 返回 [['id' => 1, 'name' => 'Alice']]
array_udiff() 允许你提供一个自定义的比较函数,这在比较复杂结构时尤其有用:
function compare_nested_array($a, $b) {
return $a == $b ? 0 : 1;
}
$result = array_udiff($a, $b, 'compare_nested_array');
这个方法让你可以自定义比较逻辑,比如你只关心 id 字段是否匹配,而不管 name 是不是一样。
如果你处理的数据量大,还可以为每个子数组生成哈希值,然后用哈希值来比较:
function get_hash($array) {
return md5(json_encode($array));
}
$a_hashed = array_map('get_hash', $a);
$b_hashed = array_map('get_hash', $b);
$diff_keys = array_diff($a_hashed, $b_hashed);
$result = [];
foreach ($diff_keys as $key => $hash) {
$result[] = $a[$key];
}
这个技巧适合数据量大的场景,不仅性能好,还能处理复杂结构。
例如你正在开发一个接口服务,用于比对两个用户权限列表的差异:
$oldPermissions = [
['module' => 'user', 'access' => 'read'],
['module' => 'admin', 'access' => 'write'],
];
$newPermissions = [
['module' => 'user', 'access' => 'read'],
];
$removed = array_diff_recursive($oldPermissions, $newPermissions);
// 你就可以据此向管理员发邮件提醒权限变更
或者你在处理配置版本差异时,也可以用这些方法判断配置项是否新增或删除。
虽然 array_diff() 在处理简单数组时非常强大,但一旦进入嵌套结构,它就显得力不从心。幸好,PHP 提供了强大的工具(比如 array_udiff()、自定义函数),我们也可以根据具体需求灵活应对。
写好数组比较逻辑,不仅能避免数据错误,还能让系统更稳定。希望你在实际项目中能巧用这些技巧,让代码更健壮!