95

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!

hakre
  • 193,403
  • 52
  • 435
  • 836
Throoze
  • 3,988
  • 8
  • 45
  • 67

10 Answers10

191

Better (and working) variant for me:

$collection3 = new ArrayCollection(
    array_merge($collection1->toArray(), $collection2->toArray())
);
pliashkou
  • 4,052
  • 2
  • 31
  • 39
  • 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? – Guy Oct 20 '13 at 18:34
  • 5
    Maybe $merged_arr = array_merge($merged_arr, $doct_collection->toArray()) ? – pliashkou Oct 21 '13 at 09:49
  • Keep the difference between numerical and assoc arrays in mind, when going this route: overwrite vs append. – kaiser Jan 17 '22 at 10:00
15

You can simply do:

$a = new ArrayCollection();
$b = new ArrayCollection();
...
$c = new ArrayCollection(array_merge((array) $a, (array) $b));
Daniel Ribeiro
  • 10,156
  • 12
  • 47
  • 79
  • 6
    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 happens](http://www.php.net/manual/en/language.types.array.php#language.types.array.casting) – greg0ire Jun 13 '13 at 14:45
  • 22
    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. – Lewis Apr 09 '14 at 13:47
  • 5
    Thank God, someone smart! – Daniel Ribeiro Apr 09 '14 at 13:55
  • 1
    @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 variant – pliashkou Apr 07 '15 at 14:29
  • 2
    @Lewis : 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. – greg0ire Sep 16 '15 at 16:40
  • 1
    Here is what the result of the cast looks like with php 5.5 : `array(1) { '\0Doctrine\Common\Collections\ArrayCollection\0elements' => array(0) { } } ` – greg0ire Sep 16 '15 at 16:41
  • 1
    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. – greg0ire Sep 16 '15 at 16:45
  • 1
    @Lewis Could you point me to some official documentation that states, that `IteratorAggregate` ([link](http://php.net/manual/de/class.iteratoraggregate.php)) 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](http://php.net/manual/de/function.iterator-to-array.php))? – flu Mar 09 '16 at 12:23
  • 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. – Byscripts Apr 21 '16 at 13:15
  • Perhaps it would work if you used `array_values` after the cast, but before the merge. – Steve Buzonas Aug 23 '16 at 03:14
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.

Matthias Brock
  • 193
  • 3
  • 6
  • 2
    Or use `$collection3 = new ArrayCollection(array_unique(array_merge($collection1->toArray(), $collection2->toArray())));` – spdionis Oct 31 '18 at 20:53
  • 1
    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. – zeros-and-ones Jul 15 '20 at 18:52
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

  • `toArray()` returns an array, you shouldn't need to typehint of another `array` surely? – Jimbo Apr 29 '15 at 13:08
  • @jimbo: you are right, but if for any reason the first `$collection->toArray()` returns `null` or `false`. You end up with a fatal error. – kanariezwart Oct 31 '16 at 09:47
  • Fair point - although if Doctrine fails converting to an array then something is badly wrong with the Doctrine codebase ;) – Jimbo Oct 31 '16 at 11:28
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;
}
Stephen Senkomago Musoke
  • 3,528
  • 2
  • 29
  • 27
  • 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! – Throoze Apr 10 '12 at 06:00
  • 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. – Stephen Senkomago Musoke Apr 10 '12 at 06:05
  • The thing is, I get my source collections dynamically, so I can't make the call the way you suggest... – Throoze Apr 10 '12 at 06:11
  • 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 application – Stephen Senkomago Musoke Apr 10 '12 at 06:20
  • You should, instead, use array_merge(). – Daniel Ribeiro Apr 10 '12 at 14:20
  • @drgomesp array_merge() does not always maintain the keys within the arrays, which are required for the Doctrine entities within the ArrayCollection class – Stephen Senkomago Musoke Apr 10 '12 at 15:03
  • Yury Pliashkou's solution is better – ioleo Sep 19 '13 at 10:20
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.

Manatax
  • 4,083
  • 5
  • 30
  • 40
  • 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? – k00ni Nov 13 '19 at 15:34
  • 1
    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. – Manatax Nov 13 '19 at 20:00
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);
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()
     ));
Tac Tacelosky
  • 3,165
  • 3
  • 27
  • 28
0

To get a unique collection:

$uniqueCollection = new ArrayCollection(array_unique(array_merge(
    $collectionA->toArray(),
    $collectionB->toArray()
), SORT_REGULAR));
Gigoland
  • 1,287
  • 13
  • 10
-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
Juja
  • 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. – k00ni Nov 13 '19 at 15:36