How to merge two php Doctrine 2 ArrayCollection()
Asked Answered
H

10

96

Is there any convenience method that allows me to concatenate two Doctrine ArrayCollection()? something like:

$collection1 = new ArrayCollection();
$collection2 = new ArrayCollection();

$collection1->add($obj1);
$collection1->add($obj2);
$collection1->add($obj3);

$collection2->add($obj4);
$collection2->add($obj5);
$collection2->add($obj6);

$collection1->concat($collection2);

// $collection1 now contains {$obj1, $obj2, $obj3, $obj4, $obj5, $obj6 }

I just want to know if I can save me iterating over the 2nd collection and adding each element one by one to the 1st collection.

Thanks!

Humanism answered 10/4, 2012 at 4:37 Comment(1)
+1 because its a common and needed methodSpermatophyte
F
196

Better (and working) variant for me:

$collection3 = new ArrayCollection(
    array_merge($collection1->toArray(), $collection2->toArray())
);
Flabellate answered 1/6, 2013 at 10:9 Comment(3)
I'm trying to do the same but into an array: array_merge($merged_arr, $doct_collection->toArray()); but am not getting either an error or is it working ($merged_arr is empty). Any ideas?Together
Maybe $merged_arr = array_merge($merged_arr, $doct_collection->toArray()) ?Flabellate
Keep the difference between numerical and assoc arrays in mind, when going this route: overwrite vs append.Seventy
M
15

You can simply do:

$a = new ArrayCollection();
$b = new ArrayCollection();
...
$c = new ArrayCollection(array_merge((array) $a, (array) $b));
Magel answered 10/4, 2012 at 14:16 Comment(10)
I don't understand why this gets so many upvotes. It is simply wrong. Casting an object to an array does not call toArray(). See what happensAtal
Whilst mob mentality is always fun, did any of you actually try this before downvoting it? ArrayCollection implements IteratorAggregate which allows you to cast the collection as an array and it will work just as expected.Agostino
Thank God, someone smart!Magel
@Lewis: It ried it before, it didn't work for me (I had no time to get into it). That is why I had to write another variantFlabellate
@Agostino : a bit late to the party, but I'm coming back to that problem, and yes I tried it, and no, it does not work, hence the downvote.Atal
Here is what the result of the cast looks like with php 5.5 : array(1) { '\0Doctrine\Common\Collections\ArrayCollection\0elements' => array(0) { } } Atal
And @DanielRibeiro : thank you for implying that I (and others) are dumb, really nice. You did not even try to defend your answer yourself. Maybe it works on your version of php, but not on mine.Atal
@Agostino Could you point me to some official documentation that states, that IteratorAggregate (link) is invoked, when casting an object that implements that interface to an array? I can't find any information about that. Maybe you meant iterator_to_array() (link)?Abaddon
This does NOT work. And casting an object implementing IteratorAggregate does NOT call any toArray() method. With this method, you will get an array containing a single "Doctrine\Common\Collections\ArrayCollection\elements" key, itself containing the data as an array.Agate
Perhaps it would work if you used array_values after the cast, but before the merge.Coenobite
M
11

If you are required to prevent any duplicates, this snippet might help. It uses a variadic function parameter for usage with PHP5.6.

/**
 * @param array... $arrayCollections
 * @return ArrayCollection
 */
public function merge(...$arrayCollections)
{
    $returnCollection = new ArrayCollection();

    /**
     * @var ArrayCollection $arrayCollection
     */
    foreach ($arrayCollections as $arrayCollection) {
        if ($returnCollection->count() === 0) {
            $returnCollection = $arrayCollection;
        } else {
            $arrayCollection->map(function ($element) use (&$returnCollection) {
                if (!$returnCollection->contains($element)) {
                    $returnCollection->add($element);
                }
            });
        }
    }

    return $returnCollection;
}

Might be handy in some cases.

Mythopoeic answered 6/9, 2015 at 21:12 Comment(3)
Or use $collection3 = new ArrayCollection(array_unique(array_merge($collection1->toArray(), $collection2->toArray())));Leticialetisha
Yeah but this and the other popular answer which does the same thing convert the entire model object to arrays, which limits the further functionality.Preemption
@spdionis: not working: Object of class App\Entity\Foo could not be converted to string. The trick is to add the SORT_REGULAR flat to an array_unique().Peroxidize
I
4
$newCollection = new ArrayCollection((array)$collection1->toArray() + $collection2->toArray()); 

This should be faster than array_merge. Duplicate key names from $collection1 are kept when same key name is present in $collection2. No matter what the actual value is

Isisiskenderun answered 10/12, 2014 at 10:53 Comment(3)
toArray() returns an array, you shouldn't need to typehint of another array surely?Radbourne
@jimbo: you are right, but if for any reason the first $collection->toArray() returns null or false. You end up with a fatal error.Isisiskenderun
Fair point - although if Doctrine fails converting to an array then something is badly wrong with the Doctrine codebase ;)Radbourne
V
2

You still need to iterate over the Collections to add the contents of one array to another. Since the ArrayCollection is a wrapper class, you could try merging the arrays of elements while maintaining the keys, the array keys in $collection2 override any existing keys in $collection1 using a helper function below:

$combined = new ArrayCollection(array_merge_maintain_keys($collection1->toArray(), $collection2->toArray())); 

/**
 *  Merge the arrays passed to the function and keep the keys intact.
 *  If two keys overlap then it is the last added key that takes precedence.
 * 
 * @return Array the merged array
 */
function array_merge_maintain_keys() {
    $args = func_get_args();
    $result = array();
    foreach ( $args as &$array ) {
        foreach ( $array as $key => &$value ) {
            $result[$key] = $value;
        }
    }
    return $result;
}
Viens answered 10/4, 2012 at 5:25 Comment(7)
What's the & operator for? is it something like in C? Well, of course this is a solution, but the behavior I expected was to have an ArrayCollection which already contained some values, and use a method (of ArrayCollection, if it exists, or an isolated procedure, like yours) to add the values of another existing ArrayCollection. Your solution requires creating a new ArrayCollection, which makes the process heavy. Thanks anyway!Humanism
The & is a pass by reference, since you do not want to change the arguments. You could rewrite the method to iterate over the collections instead. There are no arguments to this method so you can combine as many collections as you want.Viens
The thing is, I get my source collections dynamically, so I can't make the call the way you suggest...Humanism
What I meant is that you can write a method mergeCollections($collection1, $collection2) which merges the contents of $collection2 into $collection1 you can reuse the mergeCollection function else where within your applicationViens
You should, instead, use array_merge().Magel
@drgomesp array_merge() does not always maintain the keys within the arrays, which are required for the Doctrine entities within the ArrayCollection classViens
Yury Pliashkou's solution is betterGrassofparnassus
C
0

Add a Collection to an array, based on Yury Pliashkou's comment (I know it does not directly answer the original question, but that was already answered, and this could help others landing here):

function addCollectionToArray( $array , $collection ) {
    $temp = $collection->toArray();
    if ( count( $array ) > 0 ) {
        if ( count( $temp ) > 0 ) {
            $result = array_merge( $array , $temp );
        } else {
            $result = $array;
        }
    } else {
        if ( count( $temp ) > 0 ) {
            $result = $temp;
        } else {
            $result = array();
        }
    }
    return $result;
}

Maybe you like it... maybe not... I just thought of throwing it out there just in case someone needs it.

Centrifugate answered 28/8, 2014 at 23:51 Comment(2)
Its always good to have some kind of diversity in possible solutions. But in comparison to the others, i don't see the benefit in using your solution. Would you mind describing it a little bit more in detail?Blinni
I landed on this question when needing to add a collection to an array, as some other people, but my use-case required checking for empty array/collection, so I shared it here.Centrifugate
S
0

Attention! Avoid large nesting of recursive elements. array_unique - has a recursive embedding limit and causes a PHP error Fatal error: Nesting level too deep - recursive dependency?

/**
 * @param ArrayCollection[] $arrayCollections
 *
 * @return ArrayCollection
 */
function merge(...$arrayCollections) {
    $listCollections = [];
    foreach ($arrayCollections as $arrayCollection) {
        $listCollections = array_merge($listCollections, $arrayCollection->toArray());
    }

    return new ArrayCollection(array_unique($listCollections, SORT_REGULAR));
}

// using
$a = new ArrayCollection([1,2,3,4,5,6]);
$b = new ArrayCollection([7,8]);
$c = new ArrayCollection([9,10]);

$result = merge($a, $b, $c);
Shurlock answered 8/11, 2019 at 8:11 Comment(0)
B
0

Combine the spread operator to merge multiple collections, e.g. all rows in all sheets of a spreadsheet, where both $sheets and $rows are ArrayCollections and have a getRows(): Collection method

// Sheet.php
public function getRows(): Collection { return $this->rows; }

// Spreadsheet.php
public function getSheets(): Collection { return $this->sheets; }

public function getRows(): Collection
     return array_merge(...$this->getSheets()->map(
        fn(Sheet $sheet) => $sheet->getRows()->toArray()
     ));
Bouillabaisse answered 7/12, 2022 at 11:47 Comment(0)
M
0

To get a unique collection:

$uniqueCollection = new ArrayCollection(array_unique(array_merge(
    $collectionA->toArray(),
    $collectionB->toArray()
), SORT_REGULAR));
Martamartaban answered 18/8, 2023 at 20:53 Comment(0)
C
-2

Using Clousures PHP5 > 5.3.0

$a = ArrayCollection(array(1,2,3));
$b = ArrayCollection(array(4,5,6));

$b->forAll(function($key,$value) use ($a){ $a[]=$value;return true;});

echo $a.toArray();

array (size=6) 0 => int 1 1 => int 2 2 => int 3 3 => int 4 4 => int 5 5 => int 6
Censer answered 20/10, 2015 at 15:14 Comment(1)
Small hint: The part echo $a.toArray(); will surely throw an error, because toArray is not a valid function. It must be at least echo $a->toArray();. Furthermore, the output in the end should be formatted as code.Blinni

© 2022 - 2024 — McMap. All rights reserved.