3

Is there a way to differentiate 2 ArrayCollection ? (like array_diff)

today I loop on the first and check if $it->contains() match, but I think it can be refactored.

arno
  • 792
  • 14
  • 33
  • 1
    Look at http://stackoverflow.com/questions/41415034/compare-arraycollection-with-array-of-objects . You might also want to include the relevant code you've already got to your question. – Veve Feb 02 '17 at 15:49

3 Answers3

7

You can use array_diff in the following way:

$diff = array_diff($arrayCollection1->toArray(), $arrayCollection2->toArray());

$arrayCollectionDiff = new ArrayCollection($diff);
Mikhail Prosalov
  • 4,155
  • 4
  • 29
  • 41
  • 4
    Does this work for you Mikhail? For me it throws: Error: Object of class AppBundle\Entity\Sprint could not be converted to string – Guardian Nov 15 '17 at 20:36
  • Show your code, please. There is no conversion to string attempt in this code snippet. – Mikhail Prosalov Nov 16 '17 at 04:20
  • I got also that error. I think it's for non flat array. – Sela Yair Sep 20 '18 at 10:10
  • this answer works, but converting objects to arrays just to get a diff is less than ideal – zeros-and-ones Jul 15 '20 at 20:01
  • 1
    Yeah, that looks a bit bizarre, but essentially ArrayCollection is a simple wrapper around an array, so it's not a major thing to "convert" ArrayCollection into an array. Moreover, array_diff would work better in terms of performance in comparison to any manipulations with ArrayCollection, such as filtering, to produce a diff collection. – Mikhail Prosalov Oct 02 '20 at 13:25
  • 1
    doesn't work if you have a nested objects (in $arrayCollection1->toArray() or $arrayCollection2->toArray()) – Victor Bredihin Nov 09 '21 at 10:10
  • I concur with Victor. If your collections contain objects, such as entity instances, then if the count of entities in both collections is the same `array_diff` will claim there's no differences. A more in-depth comparison of the collection's object contents becomes necessary. – Adambean Mar 17 '23 at 10:37
4

I would suggest the following:

A class made of two functions:

  • function getElementFromANotInB gives the elements from ArrayCollection A not in ArrayCollection B (can also be used the other way around to get elements from B not in A);
  • function getElementCommonInAAndB gives common elements of ArrayCollection A and B.

Here is the class:

<?php
namespace [Bundle name]\DependencyInjection\ToolBox\ArrayCollectionTB;
use Doctrine\Common\Collections\ArrayCollection;
class ArrayCollectionTB {
    public function __construct(){}
    public function getElementFromANotInB(ArrayCollection $acA,ArrayCollection $acB){
        return $elementsInANotInB = $acA->filter(function($a) use ($acB) {
            return $acB->contains($a)===false;
        });
    }

    public function getElementCommonInAAndB(ArrayCollection $acA,ArrayCollection $acB){
        return $elementsCommonInAAndB = $acA->filter(function($a) use ($acB) {
            return $acB->contains($a)===true;
        });

    }
}
?>

Here is the test class that comes with it:

<?php
namespace Tests\[Bundle name]\DependencyInjection\ToolBox\ArrayCollectionTB;

use [Bundle name]\DependencyInjection\ToolBox\ArrayCollectionTB\ArrayCollectionTB;
use Doctrine\Common\Collections\ArrayCollection;

use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

/**
* @coversDefaultClass [Bundle name]\DependencyInjection\ToolBox\ArrayCollectionTB
*/
class ArrayCollectionTBTest extends KernelTestCase{

    private static $acA;
    private static $acB;
    private static $acTB;

    public static function setUpBeforeClass()
    {
        //start the symfony kernel
        $kernel = static::createKernel();
        $kernel->boot();
        //get the DI container
        $container = $kernel->getContainer();

        self::$acA = new ArrayCollection();
        self::$acB = new ArrayCollection();

        //acA and acB have in common 1,5
        //acA has 2 and 3 not in acB
        self::$acA->add('element 1');
        self::$acA->add('element 2');
        self::$acA->add('element 3');
        self::$acA->add('element 5');
        //acB has 4 and 6 not in acA
        self::$acB->add('element 1');
        self::$acB->add('element 4');
        self::$acB->add('element 5');
        self::$acB->add('element 6');

        self::$acTB = new ArrayCollectionTB();
    }

    /**
    * @covers ::getElementFromANotInB
    * @testdox test check dif give element from ArrayCollection A not in ArrayCollection B
    */
    public function testGetElementFromANotInB(){
        $res = self::$acTB->getElementFromANotInB(self::$acA,self::$acB);
        //result should be  2 and 3
        $this->assertNotContains('element 1',$res);
        $this->assertContains('element 2',$res);
        $this->assertContains('element 3',$res);
        $this->assertNotContains('element 4',$res);
        $this->assertNotContains('element 5',$res);
        $this->assertNotContains('element 6',$res);


    }

    /**
    * @covers ::getElementFromANotInB
    * @testdox test check dif give element from ArrayCollection B not in ArrayCollection A
    */
    public function testGetElementFromBNotInA(){
        $res = self::$acTB->getElementFromANotInB(self::$acB,self::$acA);
        $this->assertNotContains('element 1',$res);
        $this->assertNotContains('element 2',$res);
        $this->assertNotContains('element 3',$res);
        $this->assertContains('element 4',$res);
        $this->assertNotContains('element 5',$res);
        $this->assertContains('element 6',$res);
    }

    /**
    * @covers ::getElementCommonInAAndB
    * @testdox test check gives element from ArrayCollection A and ArrayCollection A
    */
    public function testGetElementFromBAndA(){
        $res = self::$acTB->getElementCommonInAAndB(self::$acB,self::$acA);
        $this->assertContains('element 1',$res);
        $this->assertNotContains('element 2',$res);
        $this->assertNotContains('element 3',$res);
        $this->assertNotContains('element 4',$res);
        $this->assertContains('element 5',$res);
        $this->assertNotContains('element 6',$res);
    }
}
?>
nyluje
  • 3,573
  • 7
  • 37
  • 67
0

When ArrayCollection is an array of objects:

$collFoo = ArrayCollection($arrayOfFooObjects);
$collBar = ArrayCollection($arrayOfBarObjects);

$diff = $collFoo->filter(function (Foo $fooObject) use ($collBar) {
    return !$collBar->contains($fooObject);
});

Simple array_diff throws an error: Error: Object of class Foo could not be converted to string

long
  • 3,692
  • 1
  • 22
  • 38