array_unique() 默认重排索引且不保留原始键名,需手动遍历去重以保键保序;sort_regular 可区分类型,多维数组须序列化后去重。

用 array_unique() 保留顺序但键名错乱?
array_unique() 默认会重排索引,导致数字键变成 0,1,2…,而你可能依赖原始键(比如从数据库查出的带 ID 的关联数组)。这不是 bug,是设计如此——它只保证值唯一,不承诺键结构。
解决方法很简单:用 array_values() 重排索引(如果你真需要连续数字键),或更常见的是用 array_merge() 强制重置键:
$arr = ['a' => 1, 'b' => 2, 'c' => 1]; $result = array_merge(array_unique($arr)); // $result['a'] 不存在了,键变成 0=>1, 1=>2
但注意:array_merge() 对关联键无效,它只对数字键“重编号”,所以真正想保留原始键名(如 'a'、'b'),就得手动遍历去重。
手写去重保留原始键和顺序的通用写法
PHP 没有内置函数直接支持“去重+保键+保序”,得自己控制逻辑。核心是用一个临时数组记录已见的值,跳过重复项。
立即学习“PHP免费学习笔记(深入)”;
- 用
foreach遍历原数组,检查值是否已在$seen中 - 没出现过就写入结果数组,并在
$seen标记该值 - 注意:浮点数、布尔、null 的比较要小心,
in_array($v, $seen, true)效率低,改用isset($seen[$v])不行(因为键只能是字符串或整数) - 稳妥做法是把值转成字符串再哈希,或直接用
!in_array($v, $seen, true)+ 小数组;大数组建议用array_flip()预处理
$arr = ['a'=>1, 'b'=>2, 'c'=>1, 'd'=>3];
$seen = [];
$result = [];
foreach ($arr as $k => $v) {
if (!in_array($v, $seen, true)) {
$result[$k] = $v;
$seen[] = $v;
}
}
array_unique() 的 SORT_STRING 和 SORT_REGULAR 差在哪
默认行为是 SORT_STRING,会把所有值转成字符串比较,导致 0 和 '0' 被认为相同;而 SORT_REGULAR 用 PHP 原生类型比较规则,0 === '0' 是 false,所以能区分。
- 多数情况该用
SORT_REGULAR,尤其数组含混合类型(int/string/bool) -
SORT_STRING可能引发意外合并,比如[0, '0', false]经array_unique()后只剩一个元素 - 性能上差别不大,但语义清晰更重要
$arr = [0, '0', false]; var_dump(array_unique($arr)); // [0] var_dump(array_unique($arr, SORT_REGULAR)); // [0, '0', false]
多维数组怎么去重?array_unique() 不支持
array_unique() 只能处理一维,遇到二维数组(如 [['id'=>1,'name'=>'A'], ['id'=>1,'name'=>'A']])会报 warning 并返回原数组。
必须先序列化每行再比对,或者用 json_encode() 生成唯一标识:
-
json_encode($row, JSON_UNESCAPED_UNICODE)更可靠,避免中文编码问题 - 记得加
JSON_UNESCAPED_UNICODE,否则中文变 \uXXXX,影响可读性和调试 - 如果数组含资源、闭包或循环引用,
json_encode()会失败,此时只能用serialize()(但结果不可读,且版本兼容性差)
$rows = [['id'=>1,'n'=>'A'], ['id'=>1,'n'=>'A']];
$seen = [];
$result = [];
foreach ($rows as $k => $row) {
$key = json_encode($row, JSON_UNESCAPED_UNICODE);
if (!isset($seen[$key])) {
$result[] = $row;
$seen[$key] = true;
}
}
复杂点在于:嵌套结构、浮点精度、NaN、对象等没法靠简单序列化搞定,这种场景建议提前规范数据结构,或用专门的库做 deep-compare。











