为什么 array_unique 不检测重复对象?

我似乎无法弄清楚 PHP 场景背后发生了什么魔法以及为什么array_unique无法检测到我的重复项。

在我的具体情况下,我有 2 个用户集合,我将它们合并为一个,然后只保留唯一的条目。为此,我将两个集合都转换为数组,array_merge()然后基于参数应用array_unique(..., SORT_REGULAR),以便将它们作为对象进行比较而无需任何转换。我意识到比较对象是一个滑坡,但在这种情况下它比我想象的更奇怪。

合并之后但在唯一性检查之前我有这个状态:

http://img1.mukewang.com/642924f0000129ca06590380.jpg

如您所见,第 4 项和第 11 项是相同的用户实体(非严格比较和严格比较都同意这一点)。然而,array_unique()由于某种原因,他们都留在了名单中:

http://img2.mukewang.com/64292501000152ab06570378.jpg

如您所见,第 7-10 项已被检测并删除,但第 11 项没有。

这怎么可能?我在这里没有看到什么?

当前运行 PHP 7.4.5

代码来自使用 Symfony 4.4.7 和 Doctrine ORM 2.7.2 的项目(尽管我认为这应该是无关紧要的,如果对象在比较=====比较中都是相等的)。

奖励积分的有趣事实 -array_unique连续申请两次实际上会产生独特的结果:

http://img4.mukewang.com/642925100001225f06560421.jpg

头脑=吹

更新:throw new \RuntimeException()我在我的方法中添加了User::__toString(),以额外确保没有人正在转换为字符串。

请不要建议转换为字符串 - 这既不是我的问题的解决方案,也不是这个问题的内容。


慕尼黑的夜晚无繁华
浏览 143回答 3
3回答

有只小跳蛙

array_unique对于您手头的问题,我真的怀疑这是从使用标志时从数组中删除元素的方式来的 SORT_REGULAR,方法是:整理它如果相邻项相等,则删除它们Proxy而且因为您的收藏中间确实有一个对象User,这可能会导致您遇到当前面临的问题。这似乎得到了sortPHP 文档页面警告的支持。警告对具有混合类型值的数组进行排序时要小心,因为如果是sort(),可能会产生意想不到的结果。sort_flagsSORT_REGULAR现在对于一个可能的解决方案,这可能会给你带来更多 Symfony 风味的东西。它使用ArrayCollection filterandcontains方法来过滤第二个集合,只添加第一个集合中不存在的元素。为了完全完整,该解决方案还利用use语言结构将第二个传递ArrayCollection给所需的闭包函数filter。这将导致一个新的ArrayCollection不包含重复的用户。public static function merge(Collection $a, Collection $b, bool $unique = false): Collection {  if($unique){    return new ArrayCollection(      array_merge(        $a->toArray(),        $b->filter(function($item) use ($a){          return !$a->contains($item);        })->toArray()      )    );  }  return new ArrayCollection(array_merge($a->toArray(), $b->toArray()));}

炎炎设计

我知道你说你不想转换为字符串,但我看到你还没有出路,所以我建议你对serialize数组中的每个对象使用该函数,我没有找到方法比较未转换为数组或字符串的对象(如果您不熟悉字符串或数组,则不能尝试转换为二进制或十六进制,但我不知道是否可以转换为二进制或十六进制而不转换为字符串).但是,如果你使用serialize,你可以在 php 的读取数据中序列化对象,与其他序列化对象相比,这个方法 ( serialize) 是安全的,因为你可以做一个unserialize, 并再次获得原始对象。所以你可以序列化数组中的所有元素,然后,你可以array_unique像这样使用:<?phpheader("Content-Type: application/json");class MyClass{&nbsp; &nbsp; public $var1;&nbsp; &nbsp; public $var2;&nbsp; &nbsp; function __construct($var1, $var2)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; $this->var1 = $var1;&nbsp; &nbsp; &nbsp; &nbsp; $this->var2 = $var2;&nbsp; &nbsp; }}$arr = [&nbsp; &nbsp; "a",&nbsp; &nbsp; "a",&nbsp; &nbsp; [1,2,3],&nbsp; &nbsp; "b",&nbsp; &nbsp; [1,2,3],&nbsp; &nbsp; new MyClass(1,1),&nbsp; &nbsp; new MyClass(1,new MyClass(1,1)),&nbsp; &nbsp; new MyClass(1,new MyClass(1,1)),];$arrSerilized = array_map("serialize", $arr);var_dump(&nbsp; &nbsp; array_map(&nbsp; &nbsp; &nbsp; &nbsp; "unserialize",&nbsp; &nbsp; &nbsp; &nbsp; array_unique(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $arrSerilized,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SORT_STRING&nbsp; &nbsp; &nbsp; &nbsp; )&nbsp; &nbsp; ));/* output:array(5) {&nbsp; &nbsp; [0]=>&nbsp; &nbsp; string(1) "a"&nbsp; &nbsp; [2]=>&nbsp; &nbsp; array(3) {&nbsp; &nbsp; &nbsp; &nbsp; [0]=>&nbsp; &nbsp; &nbsp; &nbsp; int(1)&nbsp; &nbsp; &nbsp; &nbsp; [1]=>&nbsp; &nbsp; &nbsp; &nbsp; int(2)&nbsp; &nbsp; &nbsp; &nbsp; [2]=>&nbsp; &nbsp; &nbsp; &nbsp; int(3)&nbsp; &nbsp; }&nbsp; &nbsp; [3]=>&nbsp; &nbsp; string(1) "b"&nbsp; &nbsp; [5]=>&nbsp; &nbsp; object(MyClass)#6 (2) {&nbsp; &nbsp; &nbsp; &nbsp; ["var1"]=>&nbsp; &nbsp; &nbsp; &nbsp; int(1)&nbsp; &nbsp; &nbsp; &nbsp; ["var2"]=>&nbsp; &nbsp; &nbsp; &nbsp; int(1)&nbsp; &nbsp; }&nbsp; &nbsp; [6]=>&nbsp; &nbsp; object(MyClass)#7 (2) {&nbsp; &nbsp; &nbsp; &nbsp; ["var1"]=>&nbsp; &nbsp; &nbsp; &nbsp; int(1)&nbsp; &nbsp; &nbsp; &nbsp; ["var2"]=>&nbsp; &nbsp; &nbsp; &nbsp; object(MyClass)#8 (2) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ["var1"]=>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int(1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ["var2"]=>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int(1)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}*/希望这对你有帮助,祝你有美好的一天!PS:你serialize可以在不同的变量类型中保存相同的值,比如1在"1"不同的php读取数据中序列化

牛魔王的故事

在不了解您的实体类的情况下,很难猜测为什么会这样。但我想你的主要问题是__toString()method 。如果您尚未定义它,则应添加一个,以便它为每个实体对象返回一个唯一/不同的字符串。如果它已经定义,请确保它返回不同的字符串。class User{&nbsp;&nbsp; &nbsp;private $name;&nbsp; &nbsp;function __construct($name){&nbsp;&nbsp; &nbsp; &nbsp; $this->name=$name;&nbsp; &nbsp;}&nbsp; &nbsp;function __toString(){&nbsp;&nbsp; &nbsp; &nbsp;return $this->name;&nbsp;&nbsp; &nbsp;}}$user = [];$users[] = new User("User1");$users[] = new User("User2");$users[] = new User("User3");$user1= $users[0];$users[]=$user1; //duplicateecho(count(array_unique($users))); // output should be 3鉴于有关实体类的信息有限,我可以猜到这里。编辑:阅读您的编辑后,我猜您将自己锁定在其中。Sincearray_unique将根据您传递的 sort_flag 尝试将实体对象转换为字符串或数字。更多关于array_unique。因此,您需要实现 __toString() 或添加一些公共属性来定义对象对实体的唯一性,例如class User{&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;public $id;&nbsp; &nbsp; &nbsp; &nbsp;private $name;&nbsp; &nbsp; &nbsp; &nbsp;function __construct($id,$name){&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $this->id=$id;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $this->name=$name;&nbsp; &nbsp; &nbsp; &nbsp;}}$user = [];$users[] = new User(1,"User1");$users[] = new User(2,"User2");$users[] = new User(3,"User3");$user1= $users[0];$users[]=$user1; //duplicateecho(count(array_unique($users, SORT_REGULAR))); // output should be 3请注意公共财产$id和SORT_REGULAR旗帜。
打开App,查看更多内容
随时随地看视频慕课网APP