pin_drop当前位置:知识文库 ❯ 图文

PHP数组去重:为什么array_unique会坑你?两种解决方案

发布日期:2026-03-12 作者:小齐智能

兄弟们,姐妹们,在PHP这片大海上航行,谁还没遇到过几个“诡异”的Bug?

前两天我写一个功能,要从数据库里拉一批用户ID出来,然后合并到一个大数组里。数据量嘛,也就几千条,很正常。处理完之后,我寻思着ID肯定有重复的啊,得去重一下。这还用想?PHP内置函数一把梭!抬手就是array_unique,干净利落,代码跑起来也是丝般顺滑,我美滋滋地就提交代码准备下班了。

结果你猜怎么着?第二天,线上的同事反馈,有个统计报表的数据怎么都对不上,总是少了几个人!

我一开始还嘴硬:“不可能!代码逻辑绝对没问题!” 然后打开服务器日志,一点一点排查。最后定位到,就是那个该死的数组去重之后,用户数量变少了!但逻辑上不应该少啊,重复的ID去掉,数量应该不变才对(ID本来就是用来计数的)。

我盯着那个array_unique看了半天,恨不得把它看出个洞。后来突然灵光一闪(主要是被坑多了的经验),我把去重前后的数组拿出来,用var_dump仔细对比了一下。这一看不要紧,我直接裂开了。

原本数组里是这种结构的:

代码示例

[    
    ['user_id' => 1001, 'score' => 85],    
    ['user_id' => 1002, 'score' => 92],    
    ['user_id' => 1001, 'score' => 78]
]

我只是想根据user_id去重,把后面那个1001的记录干掉,留下第一个或者最后一个都行。结果array_unique一上去,好家伙,不仅没给我去掉重复的user_id,数组反而变得乱七八糟,甚至有些元素直接变成null了!

这到底是人性的扭曲,还是道德的沦丧?

其实都不是,这就是array_unique这个函数的“潜规则”。咱们平时看官方文档,它说“移除数组中重复的值”。这个“值”啊,对于简单数组(比如全是数字、全是字符串)来说,确实好用。但是一旦数组变得稍微复杂一点,变成多维数组,或者数组元素是对象的时候,array_unique的“智商”就不够用了。

它默认的比较方式,是把整个数组元素(比如上面那个['user_id' => 1001, 'score' => 85])先转换成字符串,然后再比较。你想想,一个数组转换成字符串是什么?无非就是“Array”这个词!所以,在array_unique眼里,所有的子数组都变成了字符串“Array”,那当然全都是重复的啊!结果就是,它会只保留第一个子数组,然后把后面所有的“Array”都当作重复项给删了,最后你得到的可能就只剩下一个元素了。如果你数组里还混了点别的类型,那结果就更不可控了。

这感觉,就像你想用菜刀砍死一只蚊子,结果把整个厨房都给拆了。

那咋整?难道要自己写循环,一个个比吗?

写循环当然可以,而且是最稳妥的。但是,作为一个有追求的懒人程序员,我还是想找点优雅的写法。毕竟,自己写循环不仅要考虑性能,还要写好几行代码,不够优雅。

经过一番痛苦的搜索引擎之旅(和今天我要帮你解决的问题),我终于找到了几种既能解决问题,代码又看着舒服的方法,今天分享给你,尤其是第二种,我愿称之为绝活!

方案一:最“笨”但最靠谱的“建房标记法”

就是自己维护一个名字叫$seen的数组,来记录已经出现过的ID。然后在循环里判断,如果ID没出现过,就把它扔进新数组。

代码示例

<?php
$originalArray = [
    ['user_id' => 1001, 'score' => 85],
    ['user_id' => 1002, 'score' => 92],
    ['user_id' => 1001, 'score' => 78]
];
$uniqueArray = [];
$seenIds = [];
foreach ($originalArray as $item) {
    $userId = $item['user_id'];
    // 如果这个ID还没出现过
    if (!in_array($userId, $seenIds)) {
        $uniqueArray[] = $item; // 把这个完整的数组项保存起来
        $seenIds[] = $userId;    // 标记这个ID已经用过了
    }
}
// 打印看看结果
print_r($uniqueArray);
// 输出:
// Array
// (
//     [0] => Array
//         (
//             [user_id] => 1001
//             [score] => 85
//         )
//     [1] => Array
//         (
//             [user_id] => 1002
//             [score] => 92
//         )
// )
?>

这个方法逻辑清晰,你自己看,给同事看,甚至给不懂技术的产品经理看,都能看懂。代码虽然多了几行,但稳定性一流,绝对不会出现那种“诡异”的Bug。


方案二:我心中的绝活 —— array_column + array_combine 组合拳

这个方法就有点骚操作了,适合在代码review的时候秀一把。核心思想是利用数组的键(Key)是不能重复的这一特性。

先用array_column把数组中我们关心的那个字段(比如user_id)全部取出来,当作新数组的值。

再用array_column把整个原数组取出来(是的,它可以这么干),当作新数组的值。

然后用array_combine把第1步得到的值(当作键)和第2步得到的值(当作值)组合成一个新数组。

因为键名不能重复,所以重复的user_id会自动覆盖,只保留最后一个。

最后用array_values重新索引一下,完美!

看代码:

代码示例

<?php
$originalArray = [
    ['user_id' => 1001, 'score' => 85],
    ['user_id' => 1002, 'score' => 92],
    ['user_id' => 1001, 'score' => 78]
];
// 第一步:以 user_id 作为键,原数组的子数组作为值,重新组合
// array_column($originalArray, null, 'user_id') 这行是精髓!
// null 表示取整个子数组,'user_id' 表示用这个字段的值作为新数组的键
$tempArray = array_column($originalArray, null, 'user_id');
// 这时的 $tempArray 是:
// [
//     1001 => ['user_id' => 1001, 'score' => 78],  // 注意,最后一个1001覆盖了前面的
//     1002 => ['user_id' => 1002, 'score' => 92]
// ]
// 第二步:如果你不需要保留原索引,可以用 array_values 重新索引
$finalArray = array_values($tempArray);
print_r($finalArray);
// 输出:
// Array
// (
//     [0] => Array
//         (
//             [user_id] => 1001
//             [score] => 78   // 保留的是最后一个1001的记录
//         )
//     [1] => Array
//         (
//             [user_id] => 1002
//             [score] => 92
//         )
// )
?>

你品,你细品! 就一行核心代码(array_column($originalArray, null, 'user_id'))搞定了去重,而且还能控制是保留第一个出现的还是最后一个出现的(取决于你数组原来的顺序)。这感觉简直不要太爽,代码简洁,性能也杠杠的。

总结一下:

所以啊,以后再遇到PHP多维数组去重的问题,别一上来就array_unique了,它真不是万能的。

如果你想要极致的稳定性和可读性,就用方案一的循环标记法。

如果你想让代码看起来更“高级”,且想用一行代码解决问题,方案二的array_column技巧绝对是你的不二之选。这个技巧在面试中拿出来,也能让面试官眼前一亮。

好了,今天的分享就到这里。我就是那个被array_unique坑过,然后又自己爬出来的人。希望我的经历能帮你少走点弯路。如果你也有什么被PHP内置函数坑惨的经历,欢迎在评论区留言吐槽,咱们一起抱团取暖!

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python列表去重如何保持顺序?90%的人不知道的dict.fromkeys()技巧 下一篇: 无

poll相关推荐