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

95

是否有方便的方法可以让我连接两个Doctrine 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 }

我只想知道是否可以避免迭代第二个集合,并将每个元素逐个添加到第一个集合中。

谢谢!


3
+1 是因为它是一种常见且必需的方法。 - JavierIEH
10个回答

194

对我来说更好(并且可用)的变体:

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

我正在尝试将其合并到一个数组中:array_merge($merged_arr, $doct_collection->toArray()); 但是没有出现错误,也没有起作用($merged_arr为空)。有什么想法吗? - Guy
5
或许 $merged_arr = array_merge($merged_arr, $doct_collection->toArray()) ? - pliashkou
在使用这种方法时,请记住数字数组和关联数组之间的区别:覆盖 vs 追加。 - kaiser

15
你可以简单地这样做:
$a = new ArrayCollection();
$b = new ArrayCollection();
...
$c = new ArrayCollection(array_merge((array) $a, (array) $b));

6
我不明白为什么这篇文章会收到这么多点赞,它是错误的。将一个对象强制转换为数组并不会调用 toArray() 方法。可以查看此链接以了解详细信息。 - greg0ire
22
大众心理总是很有趣,但在你们给它投反对票之前,有没有人尝试过这个方法?ArrayCollection实现了IteratorAggregate接口,这使得你可以将集合转换为数组并且它会按预期工作。 - Lewis
5
谢天谢地,有个聪明人! - Daniel Ribeiro
1
@Lewis:我之前尝试过,但对我没用(我没有时间深入研究)。这就是为什么我不得不写另一个变量的原因。 - pliashkou
2
@Lewis:有点晚了,但我回来解决那个问题了。是的,我尝试过了,但不行,所以我给它点了踩。 - greg0ire
显示剩余5条评论

11

如果您需要防止任何重复项,这个代码片段可能会有所帮助。它使用可变函数参数以在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;
}

在某些情况下可能会很方便。


2
或者使用 $collection3 = new ArrayCollection(array_unique(array_merge($collection1->toArray(), $collection2->toArray()))); - spdionis
1
是的,但这个和另一个流行的答案都将整个模型对象转换为数组,从而限制了进一步的功能。 - zeros-and-ones

4
$newCollection = new ArrayCollection((array)$collection1->toArray() + $collection2->toArray()); 

这应该比array_merge更快。当在$collection2中存在相同的键名时,将保留来自$collection1的重复键名,而无论实际值是什么。


toArray() 返回一个数组,你肯定不需要再次使用 array 进行类型提示吧? - Jimbo
@jimbo:你说得没错,但如果第一个$collection->toArray()由于任何原因返回了nullfalse。那么你最终会遇到致命错误。 - kanariezwart
公平的观点 - 不过如果Doctrine无法将其转换为数组,那么Doctrine代码库肯定出现了严重问题 ;) - Jimbo

2

您仍然需要迭代 Collections 来将一个数组的内容添加到另一个数组中。由于 ArrayCollection 是一个包装类,您可以尝试合并元素数组并保持键,$collection2 中的数组键会覆盖 $collection1 中的任何现有键,使用下面的辅助函数:

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

“&” 运算符是用来做什么的?它类似于 C 语言中的吗?当然,这是一种解决方案,但我期望的行为是有一个已经包含一些值的 ArrayCollection,并使用一个方法(如果存在 ArrayCollection 的话)或一个孤立的过程(像你的过程)来添加另一个现有的 ArrayCollection 的值。你的解决方案需要创建一个新的 ArrayCollection,这使得过程变得繁重。无论如何,谢谢! - Throoze
& 是传递引用,因为您不想更改参数。您可以重写该方法以遍历集合。此方法没有参数,因此您可以组合任意数量的集合。 - Stephen Senkomago Musoke
问题是,我的源集合是动态获取的,所以我无法按照你所建议的方式进行调用... - Throoze
@drgomesp array_merge() 不总是保持数组中的键,这对于 ArrayCollection 类中的 Doctrine 实体是必需的。 - Stephen Senkomago Musoke
Yury Pliashkou的解决方案更好。 - ioleo
显示剩余2条评论

0

使用展开运算符将多个集合合并,例如电子表格中所有工作表的所有行,其中$ sheets和$ rows都是ArrayCollections,并具有getRows():Collection方法。

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

0

根据Yury Pliashkou的评论,将一个集合添加到数组中(我知道这并不直接回答原始问题,但已经有人回答了,这可能对其他人有帮助):

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

也许你会喜欢它...也许不会...我只是想把它放在这里,以防有人需要。


在可能的解决方案中拥有某种形式的多样性总是很好的。但与其他解决方案相比,我并没有看到使用您的解决方案的好处。您介绍一下更详细的内容吗? - k00ni
1
当我需要将一个集合添加到一个数组中时,我发现了这个问题,就像其他一些人一样,但我的使用情况需要检查空的数组/集合,所以我在这里分享了它。 - Manatax

0

注意!避免递归元素的大量嵌套。array_unique-具有递归嵌套限制,会导致PHP错误致命错误:嵌套级别太深-递归依赖?

/**
 * @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
获取一个独特的收藏品:
$uniqueCollection = new ArrayCollection(array_unique(array_merge(
    $collectionA->toArray(),
    $collectionB->toArray()
), SORT_REGULAR));

-2

使用闭包 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

小提示:部分 echo $a.toArray(); 一定会抛出错误,因为 toArray 不是一个有效的函数。它必须至少是 echo $a->toArray();。此外,最后的输出应该格式化为代码。 - k00ni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接