I can't seem to figure out what magic is happening behind the PHP scene and why array_unique cannot detect my duplicates.
In my specific situation, I have 2 collections of users, which I am merging into one and then keeping only unique entries. For that I am converting both collections into arrays, array_merge()
them and then based on parameter apply array_unique(..., SORT_REGULAR)
so that they are compared as objects without any conversions. I realise that comparing objects is a slippery slope, but in this case it's weirder than I though.
After merge but before the uniqueness check I have this state:
As you can see, items 4 and 11 are the same User entity (both non-strict and strict comparison agree on that). Yet after array_unique()
they both remain in the list for some reason:
As you can see, items 7-10 were detected and removed, but 11 wasn't.
How is that possible? What am I not seeing here?
Currently running PHP 7.4.5
Code is from project using Symfony 4.4.7 and Doctrine ORM 2.7.2 (although I think this should be irrelevant, if the objects are equal both by ==
and ===
comparisons).
Fun fact for bonus points - applying array_unique
twice in a row gives actually unique results:
Mind = blown
UPDATE: I have added throw new \RuntimeException()
in my User::__toString()
method, to be extra sure noone is doing conversion to string.
Please do not suggest converting to string - that is neither a solution to my problem, nor what this question is about.
The array_unique() function removes duplicate values from an array. If two or more array values are the same, the first appearance will be kept and the other will be removed. Note: The returned array will keep the first array item's key type.
The array_unique() is a built-in function in PHP and this function removes duplicate values from an array. If there are multiple elements in the array with same values then the first appearing element will be kept and all other occurrences of this element will be removed from the array.
The array_diff() (manual) function can be used to find the difference between two arrays: $array1 = array(10, 20, 40, 80); $array2 = array(10, 20, 100, 200); $diff = array_diff($array1, $array2); // $diff = array(40, 80, 100, 200);
For your issue at hand, I am really suspecting this is coming from the way
array_unique
is removing elements out of the array, when using the SORT_REGULAR
flag, by:
And because you do have a Proxy
object in the middle of your User
collection, this might cause you the issue you are currently facing.
This seems to be backed up by the warning of the sort
page of PHP documentation, as pointed out be Marvin's comment.
Warning Be careful when sorting arrays with mixed types values because
sort()
can produce unexpected results, ifsort_flags
isSORT_REGULAR
.
Source: https://www.php.net/manual/en/function.sort.php#refsect1-function.sort-notes
Now for a possible solution, this might get you something more Symfony flavoured.
It uses the ArrayCollection
filter
and contains
methods in order to filter the second collection and only add the elements not present already in the first collection.
And to be fully complete, this solution is also making use of the use
language construct in order to pass the second ArrayCollection
to the closure function needed by filter
.
This will result in a new ArrayCollection
containing no duplicated user.
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()));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With