phparraysmultidimensional-arrayfilterarray-difference

Compare two object arrays by different property names using array_udiff()


I'm trying to find the elements of one array that are not in another array in PHP. I am under the impression that I should be using the array_udiff function with a custom defined callback.

The first array is an array of objects with an id property. The second array is an array of objects with a name property. The name properties contain an ID number within the string that makes up their name.

My goal is to make sure each object's ID in the first array has a corresponding object in the second array that contains it's ID within the name. Here's my code:

<?php

class NameObj {

    public $name;

    function __construct($name){
        $this->name = $name;
    }
}

class IdObj{

    public $id;

    function __construct($id){
        $this->id = $id;
    }
}

$idArray = array(
    new IdObj(1),
    new IdObj(2),
    new IdObj(3)
);

$nameArray = array(
    new NameObj('1 - Object 1 Name'),
    new NameObj('2 - Object 2 Name')
);

function custom_diff($oId, $oName){
    $splitName = explode(' - ', $oName->name);
    $idFromName = $splitName[0];

    $id = $oId->id;

    if($idFromName == $id) return 0;
    return $idFromName > $id ? 1 : -1;
}

$missing_objects = array_udiff($idArray, $nameArray, 'custom_diff');

print_r($missing_objects);

?>

I would expect to see an array containing only the third object from the first array, but instead I get this:

PHP Notice:  Undefined property: IdObj::$name in /home/ubuntu/test2.php on line 33
PHP Notice:  Undefined property: IdObj::$name in /home/ubuntu/test2.php on line 33
PHP Notice:  Undefined property: NameObj::$id in /home/ubuntu/test2.php on line 36
PHP Notice:  Undefined property: IdObj::$name in /home/ubuntu/test2.php on line 33
PHP Notice:  Undefined property: IdObj::$name in /home/ubuntu/test2.php on line 33
Array
(
[1] => IdObj Object
    (
        [id] => 2
    )

[2] => IdObj Object
    (
        [id] => 3
    )

)

What am I missing here? Am I using the array_udiff() function incorrectly?


Solution

  • You are using it incorrectly. Specifically, you are assuming that it will always be called with an item from the first array and an item from the second array, in that order, as arguments. But PHP does not make any such guarantee.

    Now you could certainly make your comparison detect the types of its arguments and act accordingly, but that's not the best idea because it's swimming against the current. array_udiff is supposed to operate on similarly structured arrays, which these are not. Additionally, you don't really need to check each element of the first array against each element of the second to achieve your goal.

    Here's how I would do it (borrowing a bit of your code):

    $extractor = function($o) { return explode(' - ', $o->name)[0]; };
    $idsFromNames = array_flip(array_map($extractor, $nameArray));
    
    foreach ($idArray as $k => $o) {
        if (!isset($idsFromNames[$o->id])) {
            unset($idArray[$k]);
        }
    }