array_diff_key()用内置规则比较键名(===)。但有时候你需要自定义键名的比较逻辑——比如不区分大小写、忽略特殊符号、按数字大小而非字符串顺序来比。
array_diff_ukey()就是做这个的:通过用户提供的回调函数来比较键名,返回第一个数组中有、但其他数组中没有的条目。这个函数从PHP 5.1开始可用。
语法格式
array_diff_ukey(array $array1, array $array2 [, array $... ], callable $key_compare_func): array
参数说明:
| 参数 | 说明 | 必填? |
|---|---|---|
| array1 | 作为基准的数组 | 必填 |
| array2 | 用来对比的数组 | 必填 |
| array3... | 更多用来对比的数组 | 可选 |
| key_compare_func | 用户自定义的回调函数,用于比较键名 | 必填 |
返回值: 返回一个数组,包含所有在array1中但不在其他数组中的条目。
回调函数的返回值规则
回调函数接收两个要比较的键名,返回:
-
负数:第一个键名 < 第二个键名
-
0:两个键名被视为相等
-
正数:第一个键名 > 第二个键名
和array_diff_uassoc的区别
| 函数 | 比较范围 | 用途 |
|---|---|---|
| array_diff_uassoc() | 键名+值都用回调比 | 两个维度都需要自定义逻辑 |
| array_diff_ukey() | 只比键名 | 只需要自定义键名的比较规则 |
示例1:不区分大小写的键名比较
<?php
function case_insensitive_key_compare($k1, $k2) {
return strcasecmp($k1, $k2); // 不区分大小写比较字符串
}
$user_info_a = [
"USERNAME" => "代码号001",
"EMAIL" => "user001@test.com",
"level" => 5,
"SCORE" => 1280
];
$user_info_b = [
"username" => "代码号001", // 小写
"email" => "user001@test.com",
"level" => 5
];
$diff = array_diff_ukey($user_info_a, $user_info_b, "case_insensitive_key_compare");
print_r($diff);
?>
输出:
Array
(
[SCORE] => 1280
)
键名"SCORE"在第二个数组中不存在(因为第二个只有username/email/level),所以返回。"USERNAME"和"EMAIL"虽然大小写不同,但回调函数把它们视为相等,所以被排除。
示例2:忽略前缀的比较(提取数字部分)
<?php
function ignore_prefix_compare($k1, $k2) {
// 提取前缀后的数字部分进行比较
$num1 = (int)str_replace("item_", "", $k1);
$num2 = (int)str_replace("item_", "", $k2);
if ($num1 === $num2) {
return 0;
}
return ($num1 > $num2) ? 1 : -1;
}
$intory_new = [
"item_101" => "键盘",
"item_102" => "鼠标",
"item_103" => "显示器",
"item_105" => "耳机"
];
$intory_old = [
"item_101" => "键盘",
"item_102" => "鼠标",
"item_104" => "摄像头"
];
$new_only = array_diff_ukey($intory_new, $intory_old, "ignore_prefix_compare");
print_r($new_only);
?>
输出:
Array
(
[item_103] => 显示器
[item_105] => 耳机
)
item_101和item_102在旧库中存在(虽然值可能不同,但这个函数只比较键名),所以被排除。item_103和item_105是新增的。
示例3:多个数组对比
<?php
function strict_key_compare($a, $b) {
return $a <=> $b; // PHP 7+ 飞船运算符
}
$master_product = [
"p001" => "机械键盘",
"p002" => "游戏鼠标",
"p003" => "曲面显示器",
"p004" => "降噪耳机",
"p005" => "摄像头"
];
$warehouse_a = [
"p001" => "机械键盘",
"p002" => "游戏鼠标",
"p003" => "曲面显示器"
];
$warehouse_b = [
"p001" => "机械键盘",
"p004" => "降噪耳机"
];
// 找出在任何仓库中都没有的商品
$not_in_any_warehouse = array_diff_ukey($master_product, $warehouse_a, $warehouse_b, "strict_key_compare");
print_r($not_in_any_warehouse);
?>
输出:
Array
(
[p005] => 摄像头
)
p005在warehouse_a和warehouse_b中都没有出现,所以返回。
个人经验分享
-
什么时候用array_diff_ukey而不是array_diff_key?
-
键名需要忽略大小写差异
-
键名包含前缀/后缀需要剥离后比较
-
键名是版本号("v1.2" vs "v1.10")需要按数字语义比较
-
键名需要特殊规范化处理(如去掉空格、标点符号)
-
-
使用场景举例:
// 场景:比较两个系统导出的数据,其中一个系统键名带下划线,另一个不带 // 系统A: ["user_id" => 1, "user_name" => "张三"] // 系统B: ["userid" => 1, "username" => "张三"] // 自定义比较函数忽略下划线,认为user_id和userid是同一个键名 -
性能考虑
自定义回调函数会有额外的函数调用开销。如果两个数组都很大(成千上万条),这个开销会变得明显。大数据量下,建议先用array_map()或array_combine()预处理键名格式,再用array_diff_key()。 -
PHP 7+ 飞船运算符简化回调
function _compare($a, $b) { return $a <=> $b; }一行搞定,效果和内置的
===比较差不多。 -
和array_diff_uassoc容易搞混
记住名字里的"u"后面跟什么:-
array_diff_u**key**→ 只比较键名(key) -
array_diff_u**assoc**→ 比较键名和值(assoc)
-
示例4:代码号学习编程场景
<?php
// 场景:比较两个学期选课记录,键名格式不同(学期前缀)
function compare_course_keys($k1, $k2) {
// 去掉前缀 s1_、s2_ 后再比较
$clean_k1 = str_replace(["s1_", "s2_"], "", $k1);
$clean_k2 = str_replace(["s1_", "s2_"], "", $k2);
return strcasecmp($clean_k1, $clean_k2);
}
$semester1 = [
"s1_php" => "PHP基础",
"s1_mysql" => "数据库设计",
"s1_js" => "JavaScript入门",
"s1_laravel" => "Laravel框架"
];
$semester2 = [
"s2_php" => "PHP进阶", // 同课程名,不同内容
"s2_mysql" => "数据库优化",
"s2_python" => "Python基础" // 新课程
];
// 找出第一学期有但第二学期没有的课程(按课程名比较,忽略s1_/s2_前缀)
$only_in_s1 = array_diff_ukey($semester1, $semester2, "compare_course_keys");
print_r($only_in_s1);
?>
输出:
Array
(
[s1_js] => JavaScript入门
[s1_laravel] => Laravel框架
)
php和mysql在两个学期都存在(虽然值不同,但键名匹配上了),所以不返回。只有js和laravel是s1独有的。
示例5:数值类型键名的自定义比较
<?php
$scores_db = [95, 87, 92, 78, 88];
$scores_cache = [87, 92, 78];
// 键名就是索引0,1,2,3,4...
// 自定义比较:认为数值差距在1以内的键名视为相同
function fuzzy_key_compare($a, $b) {
$diff = abs($a - $b);
if ($diff <= 1) {
return 0; // 视为相等
}
return ($a > $b) ? 1 : -1;
}
$diff = array_diff_ukey($scores_db, $scores_cache, "fuzzy_key_compare");
print_r($diff);
?>
输出:
Array
(
[0] => 95
[3] => 78
[4] => 88
)
这个例子比较特殊,实际中用得少。主要是说明回调函数可以写任何逻辑,不限于字符串比较。
本节课程知识要点
-
array_diff_ukey()用自定义回调函数比较键名,返回第一个数组中独有的条目 -
和
array_diff_key()的区别:后者用内置的===比较 -
和
array_diff_uassoc()的区别:后者同时比较键名和值 -
回调函数返回0时表示两个键名被视为相等
-
可以传入多个数组进行比较
-
从PHP 5.1开始可用
-
典型场景:不区分大小写、忽略前缀/后缀、版本号比较