Search code examples
phparray-intersect

Array intersection for array of objects


I have an array of arrays of objects, and I'm trying to get the intersection of objects found in all of the inner arrays. My first attempt was to use the spread operator on the outer array and pass that to array_intersect:

array_intersect(...array_values($test))

The function array_intersect, however, uses a string-based comparison string representation:

Two elements are considered equal if and only if (string) $elem1 === (string) $elem2. In words: when the string representation is the same.

I can easily implement __toString on most of my objects however one is already using this for another purpose and it isn't guaranteed to be unique. If I have to bite the bullet and refactor, I will, but I'm just seeing if there's another way to solve this cleanly.

The keys in the array do not matter, and both outer and inner arrays are not bounded.

(This is an aggregated search result from a bunch of components that is being AND'd together if anyone is wondering).

The basic gist is this:

class sample
{
    public $id;

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

    // Comment this function out to see the error
    public function __toString()
    {
        return spl_object_hash($this);
    }
}

$obj1 = new sample('1');
$obj2 = new sample('2');
$obj3 = new sample('3');
$obj4 = new sample('4');
$obj5 = new sample('5');

$test = [
    'a' => [
        $obj1,
        $obj2,
        $obj5,
    ],
    'b' => [
        $obj2,
        $obj3,
        $obj5,
    ],
    'c' => [
        $obj2,
        $obj3,
        $obj5,
    ],
    'd' => [
        $obj2,
        $obj4,
        $obj5,
    ],
];

print_r(array_intersect(...array_values($test)));

Running that will produce exactly what I want:


Array
(
    [1] => sample Object
        (
            [id] => 2
        )

    [2] => sample Object
        (
            [id] => 5
        )

)

However, if you comment out the __toString() method you'll see the exception Object of class sample could not be converted to string


Solution

  • Okay, I spent so long writing that that my brain was able to shift thinking and I came up with an answer on my own. Not a elegant but pretty clean still, and I don't need to refactor.

    $final = array_shift($test);
    while (count($test)) {
        $to_test = array_shift($test);
        $final = array_uintersect($final, $to_test, static function ($a, $b) {
            return spl_object_hash($a) <=> spl_object_hash($b);
        });
    }
    

    This just uses array_uintersect which allows a custom comparison function and we loop over each array one at a time and compute the intersection