40

I know there is array_diff and array_udiff for comparing the difference between two arrays, but how would I do it with two arrays of objects?

array(4) {
    [0]=>
        object(stdClass)#32 (9) {
            ["id"]=>
            string(3) "205"
            ["day_id"]=>
            string(2) "12"
        }
}

My arrays are like this one, I am interested to see the difference of two arrays based on id column values.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
roflwaffle
  • 29,590
  • 21
  • 71
  • 94

5 Answers5

88

This is exactly what array_udiff is for. Write a function that compares two objects the way you would like, then tell array_udiff to use that function. Something like this:

function compare_objects($obj_a, $obj_b) {
  return $obj_a->id - $obj_b->id;
}

$diff = array_udiff($first_array, $second_array, 'compare_objects');

Or, if you're using PHP >= 5.3 you can just use an anonymous function instead of declaring a function:

$diff = array_udiff($first_array, $second_array,
  function ($obj_a, $obj_b) {
    return $obj_a->id - $obj_b->id;
  }
);
Jordan Running
  • 102,619
  • 17
  • 182
  • 182
7

Here's another option, if you want to run the diff according to object instances. You would use this as your callback to array_udiff:

function compare_objects($a, $b) {
    return strcmp(spl_object_hash($a), spl_object_hash($b));
}

You'd only want to use that if you're certain that the arrays both contain only objects - here's my personal use case.

patricksayshi
  • 1,395
  • 3
  • 13
  • 18
  • and what about `return $a === $b ? 0 : -1` ? Wouldn't it do the same by comparing object variables pointing to the same object instance? – Dimitry K Apr 07 '17 at 10:39
  • 3
    The internals of this function seem to use some sorting, so you really have to return -1 or 1 when the objects are not equal, so @Dimitry K's comment is not correct. – fietserwin Jun 07 '18 at 08:17
6

And here is another option if you wanna compare string properties (e.g. name):

$diff = array_udiff($first_array, $second_array,
  function ($obj_a, $obj_b) {
    return strcmp($obj_a->name, $obj_b->name);
  }
);
megastruktur
  • 593
  • 1
  • 7
  • 9
0

Here is my take on this

/**
     * Compare two objects (active record models) and return the difference. It wil skip ID from both objects as 
     * it will be obviously different
     * Note: make sure that the attributes of the first object are present in the second object, otherwise
     * this routine will give exception.
     * 
     * @param object $object1
     * @param object $object2
     * 
     * @return array difference array in key-value pair, empty array if there is no difference
     */
    public static function compareTwoObjects($object1, $object2)
    {
        $differences=[];
        foreach($object1->attributes as $key => $value) {
            if($key =='id'){
                continue;
            }
            if($object1->$key != $object2->$key){
                $differences[$key] = array(
                                            'old' => $object1->$key,
                                            'new' => $object2->$key
                                        );
            }
        }
        return $differences;
    }
Aditya
  • 133
  • 2
  • 11
0

You have two arrays containing objects -- using array_udiff() is certainly appropriate for this task.

I'll demonstrate this technique with modern syntax ("spaceship operator" and "arrow function syntax").

Consider this sample dataset:

$array1 = [
    (object) ['id' => '204', 'day_id' => '12'],
    (object) ['id' => '205', 'day_id' => '13'],
    (object) ['id' => '206', 'day_id' => '14'],
    (object) ['id' => '207', 'day_id' => '15'],
];
$array2 = [
    (object) ['id' => '203', 'day_id' => '11'],
    (object) ['id' => '205', 'day_id' => '13'],
    (object) ['id' => '207', 'day_id' => '14'],
    (object) ['id' => '209', 'day_id' => '17'],
];

The returned data from array_udiff() will be the rows from the first array that are not matched in the second array.

Code: (Demo)

var_export(
    array_udiff(
        $array1,
        $array2,
        fn($a, $b) => $a->id <=> $b->id
    )
);

returns these two rows:

[
    (object) ['id' => '204', 'day_id' => '12'],
    (object) ['id' => '206', 'day_id' => '14'],
]

Reversing the order of the first two parameters: (Demo)

[
    (object) ['id' => '203', 'day_id' => '11'],
    (object) ['id' => '209', 'day_id' => '17'],
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136