Skip to content

Commit

Permalink
Merge pull request #252 from VincentLanglet/addNewMethods
Browse files Browse the repository at this point in the history
[2.0] Add new methods Collection::findFirst and Collection::reduce
  • Loading branch information
greg0ire authored Jun 10, 2021
2 parents a519bec + cdb4f63 commit 534712a
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 0 deletions.
24 changes: 24 additions & 0 deletions docs/en/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,18 @@ Tests for the existence of an element that satisfies the given predicate.
return $value === 'first';
}); // true
findFirst
---------

Returns the first element of this collection that satisfies the given predicate.

.. code-block:: php
$collection = new Collection([1, 2, 3, 2, 1]);
$one = $collection->findFirst(function(int $key, int $value): bool {
return $value > 2 && $key > 1;
}); // 3
filter
------

Expand Down Expand Up @@ -217,6 +229,18 @@ Applies the given function to each element in the collection and returns a new c
return $value + 1;
}); // [2, 3, 4]
reduce
------

Applies iteratively the given function to each element in the collection, so as to reduce the collection to a single value.

.. code-block:: php
$collection = new ArrayCollection([1, 2, 3]);
$reduce = $collection->reduce(function(int $accumulator, int $value): int {
return $accumulator + $value;
}, 0); // 6
next
----

Expand Down
20 changes: 20 additions & 0 deletions lib/Doctrine/Common/Collections/AbstractLazyCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,16 @@ public function exists(Closure $p): bool
return $this->collection->exists($p);
}

/**
* {@inheritDoc}
*/
public function findFirst(Closure $func)
{
$this->initialize();

return $this->collection->findFirst($func);
}

/**
* @return Collection<mixed>
*
Expand Down Expand Up @@ -237,6 +247,16 @@ public function map(Closure $func): Collection
return $this->collection->map($func);
}

/**
* {@inheritDoc}
*/
public function reduce(Closure $func, $initial = null)
{
$this->initialize();

return $this->collection->reduce($func, $initial);
}

/**
* {@inheritDoc}
*/
Expand Down
23 changes: 23 additions & 0 deletions lib/Doctrine/Common/Collections/ArrayCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use function array_key_exists;
use function array_keys;
use function array_map;
use function array_reduce;
use function array_reverse;
use function array_search;
use function array_slice;
Expand Down Expand Up @@ -349,6 +350,14 @@ public function map(Closure $func): Collection
return $this->createFrom(array_map($func, $this->elements));
}

/**
* {@inheritDoc}
*/
public function reduce(Closure $func, $initial = null)
{
return array_reduce($this->elements, $func, $initial);
}

/**
* {@inheritDoc}
*
Expand All @@ -361,6 +370,20 @@ public function filter(Closure $p): Collection
return $this->createFrom(array_filter($this->elements, $p, ARRAY_FILTER_USE_BOTH));
}

/**
* {@inheritDoc}
*/
public function findFirst(Closure $p)
{
foreach ($this->elements as $key => $element) {
if ($p($key, $element)) {
return $element;
}
}

return null;
}

public function forAll(Closure $p): bool
{
foreach ($this->elements as $key => $element) {
Expand Down
29 changes: 29 additions & 0 deletions lib/Doctrine/Common/Collections/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,19 @@ public function next();
*/
public function exists(Closure $p): bool;

/**
* Returns the first element of this collection that satisfies the predicate p.
*
* @param Closure $p The predicate.
*
* @return mixed The first element respecting the predicate,
* null if no element respects the predicate.
*
* @psalm-param Closure(TKey=, T=):bool $p
* @psalm-return T|null
*/
public function findFirst(Closure $p);

/**
* Returns all the elements of this collection that satisfy the predicate p.
* The order of the elements is preserved.
Expand Down Expand Up @@ -248,6 +261,22 @@ public function forAll(Closure $p): bool;
*/
public function map(Closure $func): self;

/**
* Applies iteratively the given function to each element in the collection,
* so as to reduce the collection to a single value.
*
* @param mixed $initial
*
* @return mixed
*
* @psalm-template TReturn
* @psalm-template TInitial
* @psalm-param Closure(TReturn|TInitial|null, T):(TInitial|TReturn) $func
* @psalm-param TInitial|null $initial
* @psalm-return TReturn|TInitial|null
*/
public function reduce(Closure $func, $initial = null);

/**
* Partitions this collection in two collections according to a predicate.
* Keys are preserved in the resulting collections.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,26 @@ public function testExists(): void
}), 'Element not exists');
}

public function testFindFirst(): void
{
$elements = [1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0];
$collection = $this->buildCollection($elements);

self::assertSame('a', $collection->findFirst(static function ($key, $element) {
return $key === 'A' && $element === 'a';
}), 'Element exists');
}

public function testFindFirstNotFound(): void
{
$elements = [1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0];
$collection = $this->buildCollection($elements);

self::assertNull($collection->findFirst(static function ($key, $element) {
return $key === 'non-existent' && $element === 'non-existent';
}), 'Element does not exists');
}

public function testIndexOf(): void
{
$elements = [1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0];
Expand Down
33 changes: 33 additions & 0 deletions tests/Doctrine/Tests/Common/Collections/BaseCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,26 @@ public function testExists(): void
self::assertFalse($exists);
}

public function testFindFirst(): void
{
$this->collection->add('one');
$this->collection->add('two');
$one = $this->collection->findFirst(static function ($k, $e) {
return $e === 'one';
});
self::assertSame('one', $one);
}

public function testFindFirstNotFound(): void
{
$this->collection->add('one');
$this->collection->add('two');
$other = $this->collection->findFirst(static function ($k, $e) {
return $e === 'other';
});
self::assertNull($other);
}

public function testMap(): void
{
$this->collection->add(1);
Expand All @@ -56,6 +76,19 @@ public function testMap(): void
self::assertEquals([2, 4], $res->toArray());
}

public function testReduce(): void
{
$this->collection->add(1);
$this->collection->add(2);
$this->collection->add(3);
$this->collection->add(4);

$res = $this->collection->reduce(static function ($sum, $e) {
return $sum + $e;
});
self::assertSame(10, $res);
}

public function testFilter(): void
{
$this->collection->add(1);
Expand Down

0 comments on commit 534712a

Please sign in to comment.