在PHP的数组函数体系中,array_udiff()属于差集运算家族中比较特殊的一个。它不依赖PHP内置的比较规则,而是把比较逻辑交给开发者自定义的回调函数。这个函数从PHP5.1.0开始提供,适合处理那些用默认比较方式无法满足需求的场景,比如对象数组、大小写敏感度需要控制的字符串数组,或者需要按特定业务规则判断“相等”的数据。
语法与参数说明
array array_udiff(
array $array1,
array $array2,
[, array $... ],
callable $value_compare_func
);
参数含义如下:
| 参数 | 说明 | 是否必须 |
|---|---|---|
| array1 | 基准数组,差集运算以此数组为起点 | 是 |
| array2 | 参与比较的数组 | 是 |
| array3,... | 更多参与比较的数组 | 否 |
| value_compare_func | 用户自定义的比较回调函数 | 是 |
回调函数的签名要求是:接收两个参数,返回一个整数。如果第一个参数小于第二个参数,返回小于0的整数;相等返回0;大于返回大于0的整数。这一点和strcmp()的返回逻辑一致。
返回值与键名处理
array_udiff()返回一个数组,包含所有在array1中但不在其他任何数组中的元素。这里有一个容易被忽略的细节:返回结果会保留array1中的键名。这意味着如果array1是关联数组,返回的数组也是关联数组,键名原样保留。
为什么需要自定义回调
PHP本身有array_diff()、array_diff_assoc()等函数,它们用PHP默认的字符串比较规则来判断元素是否相等。但在项目中,经常遇到以下情况:
-
数组元素是对象,需要按对象某个属性来判断是否相等
-
字符串比较需要区分大小写,或者需要忽略大小写
-
数值比较需要按精度处理,比如浮点数保留两位小数后再比较
-
业务上“相等”的定义不是值相等,而是某个标识符相等
这些场景下,array_udiff()的回调机制就派上了用场。
代码号学习编程:基础示例
先看一个最简单的例子,理解回调函数的工作方式。
<?php
function compareValues($a, $b)
{
if ($a === $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
}
$array1 = ["a" => "C", "b" => "C++", "d" => "Java", "r" => "HTML"];
$array2 = ["a" => "C", "y" => "C++", "d" => "C#", "x" => "PHP"];
$result = array_udiff($array1, $array2, "compareValues");
print_r($result);
输出:
Array
(
[d] => Java
[r] => HTML
)
array1中的"C"和"C++"在array2中存在,所以被排除。"Java"和"HTML"不在array2中,保留下来。注意键名d和r被保留。
多个数组参与比较
array_udiff()支持同时比较两个以上的数组。只要某个元素在任意一个后续数组中出现,就会被排除。
<?php
function compareValues($a, $b)
{
if ($a === $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
}
$lang1 = ["a" => "javatpoint", "b" => "c", "c" => "ruby", "php"];
$lang2 = ["A" => "javatpoint", "b" => "C", "php", "python"];
$lang3 = ["a" => "c", "b" => "javatpoint", "php", "python"];
$result = array_udiff($lang1, $lang2, $lang3, "compareValues");
print_r($result);
输出:
Array
(
[c] => ruby
)
javatpoint在lang2和lang3中都出现,排除。c在lang3中出现,排除。php在lang2和lang3中都出现,排除。只有ruby在所有后续数组中都不存在。
项目实战反思:一个踩坑经历
早期做电商项目时,需要对比两个商品SKU列表,找出新增的SKU。SKU数据是从两个不同接口获取的,结构不一致,但都有一个sku_code字段可以唯一标识。
最初用了array_diff(),结果发现它比较的是整个数组元素的字符串表示,而两个接口返回的数组结构不同,比较结果不可用。后来改用array_udiff(),在回调里只比较sku_code:
<?php
function compareSku($a, $b)
{
return strcmp($a['sku_code'], $b['sku_code']);
}
$oldSkus = [
['sku_code' => 'A001', 'name' => '商品A'],
['sku_code' => 'A002', 'name' => '商品B'],
];
$newSkus = [
['sku_code' => 'A002', 'name' => '商品B'],
['sku_code' => 'A003', 'name' => '商品C'],
];
$added = array_udiff($newSkus, $oldSkus, 'compareSku');
print_r($added);
输出:
Array
(
[1] => Array
(
[sku_code] => A003
[name] => 商品C
)
)
这里有一个关键点:回调函数必须返回整数,不能返回布尔值。strcmp()正好满足这个要求。如果自己写比较逻辑,记得用===判断相等,用三元运算符返回1或-1。
另一个坑是键名保留。当时把array_udiff()的结果直接传给前端,前端按索引取值,结果因为键名不连续导致数据错乱。后来加了一步array_values()重新索引,问题才解决。所以如果后续代码依赖连续数字索引,记得手动处理。
个人建议:什么时候用,什么时候不用
array_udiff()的回调机制灵活,但性能上比array_diff()要差一些,因为每个元素都要经过用户函数调用。如果只是简单的字符串或数值比较,直接用array_diff()更高效。
适合用array_udiff()的场景:
-
对象数组按属性比较
-
多维数组按某个字段比较
-
需要自定义相等规则的业务逻辑
-
大小写敏感或需要忽略大小写的比较
不适合的场景:
-
简单的标量数组差集,用
array_diff()即可 -
需要保留键名关联的差集,考虑
array_diff_assoc() -
数据量极大且对性能敏感,考虑先用哈希表预处理再比较
本节课程知识要点
-
array_udiff()用回调函数比较数组元素,计算第一个数组与其他数组的差集 -
回调函数必须返回整数,小于0、等于0、大于0分别表示小于、等于、大于
-
返回结果保留第一个数组的键名,必要时用
array_values()重新索引 -
支持两个以上数组同时比较,元素在任意后续数组中出现即被排除
-
适合对象数组、多维数组、自定义相等规则的场景,简单标量比较优先用
array_diff()
进阶示例:大小写不敏感比较
<?php
function compareCaseInsensitive($a, $b)
{
$a = strtolower($a);
$b = strtolower($b);
if ($a === $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
}
$array1 = ["Apple", "Banana", "Cherry"];
$array2 = ["apple", "banana", "date"];
$result = array_udiff($array1, $array2, "compareCaseInsensitive");
print_r($result);
输出:
Array
(
[2] => Cherry
)
Apple和Banana在忽略大小写后与array2中的元素相等,被排除。Cherry不在array2中,保留。
这个例子展示了回调函数的灵活性:比较规则由开发者决定,array_udiff()只负责遍历和筛选。
array_udiff()是PHP数组差集函数中比较强大但容易被忽视的一个。它的价值在于把比较逻辑开放给开发者,让差集运算不再局限于PHP内置的字符串比较规则。理解回调函数的返回值约定、键名保留特性以及性能边界,能在项目中更准确地选用这个函数。