array_unique对多维数组无效,因其底层用serialize()比较且PHP中数组不能作key;去重关联数组需手动遍历+哈希表;SORT_STRING易致类型混淆,应避免滥用。

array_unique 为什么对多维数组无效
array_unique 只能处理「一维」或「标量值」(string、int、float、bool)组成的数组,遇到嵌套数组(如 [['id'=>1], ['id'=>1]])会直接把整个子数组当成 array 类型,而 PHP 中数组不能作为 key 或被直接比较,所以内部用 == 判断时会强制转为 NULL,最终所有子数组都被视为“相同”,只保留第一个——看起来像“没去重”,其实是“全去重只剩一个”。
- 它底层依赖
serialize()后的字符串对比(PHP 7+),但对含资源、对象、闭包的数组会出错或跳过 - 若数组键是字符串且含非 ASCII 字符(如中文键名),
serialize()结果可能因编码不一致导致误判 - 不要试图传入
JSON字符串再用array_unique:它不解析 JSON,只会当普通字符串比对
如何正确去重关联数组(按某个字段)
比如要去掉 $users = [['name'=>'Alice','age'=>30], ['name'=>'Bob','age'=>25], ['name'=>'Alice','age'=>30]] 中 name 重复的项,得自己控制逻辑:
$seen = [];
$unique = [];
foreach ($users as $item) {
$key = $item['name']; // 按 name 去重
if (!isset($seen[$key])) {
$seen[$key] = true;
$unique[] = $item;
}
}
- 用
$seen数组做哈希表,时间复杂度 O(n),比反复调用in_array()快得多 - 如果需保留原始键名,把
$unique[] = $item改成$unique[key($users)] = $item并在循环中用each()或foreach ($users as $k => $v) - 注意
$item['name']为空、null或 0 时,isset()仍为 true;若需严格区分,改用array_key_exists()或检查!empty($item['name'])
为什么启用 SORT_STRING 后结果更奇怪
array_unique($arr, SORT_STRING) 会让函数先把所有值转成字符串再比对。对数字数组看似有用(比如 [1, '1', 1.0] 全变成 '1' 算重复),但副作用明显:
- 布尔值
true→'1',false→'',和整数 1 / 0 混淆 - 浮点数如
0.0000001转字符串后精度丢失,可能和0被判等 - 对象、
NULL、资源转字符串后都是固定字面量(如''或'Object id #123'),极易误去重
除非你明确知道输入全是干净字符串且需忽略类型差异,否则别碰这个 flag。
立即学习“PHP免费学习笔记(深入)”;
替代方案:用 array_column + array_keys 组合去重
当目标是按某字段提取唯一值(比如取所有不重复的 user_id),比手写循环更简洁:
$ids = array_column($users, 'user_id'); $unique_ids = array_keys(array_flip($ids));
-
array_flip会把值变键、键变值,自动丢弃重复键(PHP 数组键必须唯一) -
array_keys再把键取回来,就是去重后的值列表 - 注意:
array_column在 PHP 5.5+ 才支持对象数组;若字段可能为null或缺失,加第三个参数设默认值,避免产生NULL键
真正麻烦的是既要按字段去重、又要保留完整子数组结构——这时候绕不开显式遍历,array_unique 从设计上就不支持这种需求。



















