Reputation: 171
This is probably really simple, but I cannot seem to get it. I have two arrays of objects $a
and $b
. In $a
I have objects with key email
and in $b
I have objects with user_email
(this cannot be changed as it comes from an API). I want as output a third array $c
that has all the objects where email == user_email
. I've tried using array_udiff
like this:
$c = array_udiff($a, $b,
function ($obj_a, $obj_b) {
return $obj_a->email - $obj_b->user_email;
}
);
For some reason, $obj_b is not always an object from array $b
as I would have thought. Is there any clean solution for this?
Upvotes: 0
Views: 101
Reputation: 47971
Just like with usort()
functions, the array_uintersect()
and array_udiff()
family of functions do not guarantee that the $a
and $b
parameters in the callback signature relate to the first and second input arrays. In fact, these functions allow for more than 2 input arrays, but the number of parameters in the callback signature do not increase.
To resolve the issue you are encountering, use the null coalescing operator to fallback to the opposing object's property if the first attempted property access fails.
Code: (Demo)
$emails = [
(object) ['email' => 'a'],
(object) ['email' => 'b'],
(object) ['email' => 'c']
];
$userEmails = [
(object) ['user_email' => 'c'],
(object) ['user_email' => 'a'],
(object) ['user_email' => 'd']
];
var_export(
array_udiff(
$emails,
$userEmails,
fn($a, $b) => ($a->email ?? $a->user_email) <=> ($b->email ?? $b->user_email)
)
);
Result:
array (
1 =>
(object) array(
'email' => 'b',
),
)
Upvotes: 0
Reputation: 2844
You are probably looking for array_uintersect. Also, you should compare your strings with strcmp or even better with strcasecmp. Remember that the order in which PHP will pass array elements to the callback is not always the same as the order of arrays.
$a = [(object)['email' => 'a'], (object)['email' => 'b'], (object)['email' => 'c']];
$b = [(object)['user_email' => 'c'], (object)['user_email' => 'a'], (object)['user_email' => 'd']];
$comparer = function($obj_a, $obj_b) {
$email_a = property_exists($obj_a, 'email')
? $obj_a->email
: $obj_a->user_email;
$email_b = property_exists($obj_b, 'email')
? $obj_b->email
: $obj_b->user_email;
return strcasecmp($email_a, $email_b);
};
// only objects with email property
$c = array_uintersect($a, $b, $comparer);
// both objects with email and user_email property
$d = array_merge(
array_uintersect($a, $b, $comparer),
array_uintersect($b, $a, $comparer)
);
Testing with property_exists
can be changed to testing with instanceof
if the arguments are concrete classes.
Upvotes: 1