array_unique对多维数组无效,因其底层将子数组转为字符串"Array"导致误判;推荐用array_column+array_keys按单字段去重,多字段则用serialize构造唯一签名。

PHP array_unique 为什么对多维数组无效
array_unique 默认只支持一维数组,底层用字符串哈希比对值。遇到多维数组时,每个子数组会被强制转成字符串 "Array",结果所有项都变成相同字符串,最终只剩第一个 —— 看似“去重”了,实则逻辑错误,数据全丢。
这不是函数 bug,是设计如此。官方文档明确写了:「array_unique 仅适用于一维数组」。
所以别硬套,得换思路。
用 array_column + array_keys 按单字段去重(推荐)
这是最轻量、可读性高、性能也够用的方案,适用于「按某个键(如 id、email)保留首次出现项」的常见场景。
立即学习“PHP免费学习笔记(深入)”;
- 先用
array_column($arr, 'field')提取所有目标字段值成一维数组 - 再用
array_keys(array_flip(...))或array_unique(..., SORT_REGULAR)去重并保留键位 - 最后用这些键从原数组里取出对应项
示例:
$data = [
['id' => 1, 'name' => 'Alice'],
['id' => 2, 'name' => 'Bob'],
['id' => 1, 'name' => 'Alice2'],
['id' => 3, 'name' => 'Charlie']
];
$ids = array_column($data, 'id');
$uniqueKeys = array_keys(array_flip($ids)); // 保留首次出现的键
$result = array_intersect_key($data, array_flip($uniqueKeys));
注意:array_flip 要求值可作为键(即不能是数组、对象),所以字段必须是标量(int/string/bool/null)。如果字段可能为 null 或空字符串,需提前过滤或改用 foreach 手动判重。
需要按多个字段组合去重?用 foreach + 序列化键
比如要去重条件是 ['user_id', 'action', 'date'] 三者完全一致才算重复,就得自己构造唯一签名。
- 不要直接拼接字符串(
$user_id . $action . $date),容易因类型或边界值冲突(如12+'3'和1+'23'结果一样) - 推荐用
serialize([$v['user_id'], $v['action'], $v['date']])或json_encode([...], JSON_UNESCAPED_UNICODE) - 用一个临时
$seen = []数组存已见过的签名,跳过重复项
性能上,序列化比字符串拼接略慢,但安全;若数据量极大(>10 万行),可考虑改用 spl_object_hash 配合自定义对象封装,但绝大多数业务场景没必要。
为什么不用 usort + 相邻比较?
有人想先排序再遍历去重,逻辑上可行,但实际踩坑多:
- 排序本身是 O(n log n),比线性扫描 O(n) 慢
- PHP 的
usort不稳定,相同字段值的元素相对顺序可能变,影响“保留首次出现”的需求 - 代码更长,易出错,比如漏处理边界(首尾元素)、比较函数返回值没严格用 -1/0/1
除非你本来就要排序,否则纯为去重加一层排序,纯属绕路。
真正难的不是写几行代码,而是想清楚:字段是否允许为空?是否要区分大小写?去重后顺序要不要保持原始插入顺序?这些细节一旦忽略,测试环境没问题,上线后数据就悄悄不对了。



















