如何合并两个PHP Doctrine 2 ArrayCollection()


80

有什么方便的方法可以让我串联两个教义ArrayCollection()吗?就像是:

$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!


3
+1 because its a common and needed method
JavierIEH

Answers:


168

Better (and working) variant for me:

$collection3 = new ArrayCollection(
    array_merge($collection1->toArray(), $collection2->toArray())
);

Best answer, no doubt.
carles

I recommend this solution
ioleo

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

5
Maybe $merged_arr = array_merge($merged_arr, $doct_collection->toArray()) ?
pliashkou

12

You can simply do:

$a = new ArrayCollection();
$b = new ArrayCollection();
...
$c = new ArrayCollection(array_merge((array) $a, (array) $b));

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
greg0ire

21
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

5
Thank God, someone smart!
Daniel Ribeiro

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

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

10

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.


1
Or use $collection3 = new ArrayCollection(array_unique(array_merge($collection1->toArray(), $collection2->toArray())));
spdionis

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

3
$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

@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

Fair point - although if Doctrine fails converting to an array then something is badly wrong with the Doctrine codebase ;)
Jimbo

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;
}

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

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

The thing is, I get my source collections dynamically, so I can't make the call the way you suggest...
Throoze

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

You should, instead, use array_merge().
Daniel Ribeiro

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.


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

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

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);

-1

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

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.