Skip to content

Commit

Permalink
Merge pull request #49 from decole/feature/support-min-collection
Browse files Browse the repository at this point in the history
Add support for min and minBy to collections
  • Loading branch information
whsv26 authored Aug 23, 2022
2 parents 8491933 + 54c0706 commit f08c58f
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 3 deletions.
21 changes: 21 additions & 0 deletions src/Fp/Collections/ArrayList.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
use Fp\Operations\MapValuesOperation;
use Fp\Operations\MaxByElementOperation;
use Fp\Operations\MaxElementOperation;
use Fp\Operations\MinByElementOperation;
use Fp\Operations\MinElementOperation;
use Fp\Operations\MkStringOperation;
use Fp\Operations\PrependedAllOperation;
use Fp\Operations\PrependedOperation;
Expand Down Expand Up @@ -627,4 +629,23 @@ public function maxBy(callable $callback): Option
{
return MaxByElementOperation::of($this->getIterator())($callback);
}

/**
* @inheritDoc
* @return Option<TV>
*/
public function min(): Option
{
return MinElementOperation::of($this->getIterator())();
}

/**
* @inheritDoc
* @psalm-param callable(TV): mixed $callback
* @return Option<TV>
*/
public function minBy(callable $callback): Option
{
return MinByElementOperation::of($this->getIterator())($callback);
}
}
21 changes: 21 additions & 0 deletions src/Fp/Collections/LinkedList.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
use Fp\Operations\MapValuesOperation;
use Fp\Operations\MaxByElementOperation;
use Fp\Operations\MaxElementOperation;
use Fp\Operations\MinByElementOperation;
use Fp\Operations\MinElementOperation;
use Fp\Operations\MkStringOperation;
use Fp\Operations\PrependedAllOperation;
use Fp\Operations\ReduceOperation;
Expand Down Expand Up @@ -639,4 +641,23 @@ public function maxBy(callable $callback): Option
{
return MaxByElementOperation::of($this->getIterator())($callback);
}

/**
* @inheritDoc
* @return Option<TV>
*/
public function min(): Option
{
return MinElementOperation::of($this->getIterator())();
}

/**
* @inheritDoc
* @psalm-param callable(TV): mixed $callback
* @return Option<TV>
*/
public function minBy(callable $callback): Option
{
return MinByElementOperation::of($this->getIterator())($callback);
}
}
27 changes: 26 additions & 1 deletion src/Fp/Collections/SeqTerminalOps.php
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,36 @@ public function max(): Option;
*
* ```php
* >>> LinkedList::collect([new Foo(1), new Bar(6), new Foo(2)])->maxBy(fn(Foo $foo) => $foo->a)->get();
* => 6
* => Bar(6)
* ```
*
* @psalm-param callable(TV): mixed $callback
* @psalm-return Option<TV>
*/
public function maxBy(callable $callback): Option;

/**
* Returns the minimum value from collection
*
* ```php
* >>> LinkedList::collect([1, 4, 2])->min()->get();
* => 1
* ```
*
* @psalm-return Option<TV>
*/
public function min(): Option;

/**
* Returns the minimum value from collection by iterating each element using the callback
*
* ```php
* >>> LinkedList::collect([new Foo(1), new Bar(6), new Foo(2)])->minBy(fn(Foo $foo) => $foo->a)->get();
* => Foo(1)
* ```
*
* @psalm-param callable(TV): mixed $callback
* @psalm-return Option<TV>
*/
public function minBy(callable $callback): Option;
}
1 change: 0 additions & 1 deletion src/Fp/Operations/MaxByElementOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,4 @@ public function __invoke(callable $by): Option

return FirstOperation::of($gen)();
}

}
2 changes: 1 addition & 1 deletion src/Fp/Operations/MaxElementOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ public function __invoke(): Option

return Option::fromNullable(!empty($list) ? max($list) : null);
}
}
}
35 changes: 35 additions & 0 deletions src/Fp/Operations/MinByElementOperation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Fp\Operations;

use Fp\Functional\Option\Option;

/**
* @psalm-immutable
* @template TK
* @template TV
* @extends AbstractOperation<TK, TV>
*/
class MinByElementOperation extends AbstractOperation
{
/**
* @psalm-param callable(TV): mixed $by
* @return Option<TV>
*/
public function __invoke(callable $by): Option
{
$f =
/**
* @param TV $r
* @param TV $l
* @return int
*/
fn(mixed $r, mixed $l): int => $by($r) <=> $by($l);

$gen = SortedOperation::of($this->gen)($f);

return FirstOperation::of($gen)();
}
}
27 changes: 27 additions & 0 deletions src/Fp/Operations/MinElementOperation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Fp\Operations;

use Fp\Functional\Option\Option;
use function Fp\Cast\asList;

/**
* @psalm-immutable
* @template TK
* @template TV
* @extends AbstractOperation<TK, TV>
*/
class MinElementOperation extends AbstractOperation
{
/**
* @return Option<TV>
*/
public function __invoke(): Option
{
$list = asList($this->gen);

return Option::fromNullable(!empty($list) ? min($list) : null);
}
}
72 changes: 72 additions & 0 deletions tests/Runtime/Interfaces/Seq/SeqOpsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -657,4 +657,76 @@ public function testMaxByInEmptyCollection(Seq $seq, Option $option): void
{
$this->assertEquals($option, $seq->maxBy(fn(Foo $foo) => $foo->a));
}

public function provideTestMinNotEmptyCollections(): Generator
{
yield ArrayList::class => [ArrayList::collect([3, 7, 2]), Option::some(2)];
yield LinkedList::class => [LinkedList::collect([9, 1, 2]), Option::some(1)];
}

/**
* @dataProvider provideTestMinNotEmptyCollections
*/
public function testMinInNotEmptyCollection(Seq $seq, Option $option): void
{
$this->assertEquals($option, $seq->min());
}

public function provideTestMinEmptyCollections(): Generator
{
yield ArrayList::class => [ArrayList::collect([]), Option::none()];
yield LinkedList::class => [LinkedList::collect([]), Option::none()];
}

/**
* @dataProvider provideTestMinEmptyCollections
*/
public function testMinInEmptyCollection(Seq $seq, Option $option): void
{
$this->assertEquals($option, $seq->min());
}

public function provideTestMinByNotEmptyCollections(): Generator
{
yield ArrayList::class => [
ArrayList::collect([new Foo(1), new Foo(5), new Foo(2)]),
Option::some(new Foo(1))
];
yield LinkedList::class => [
LinkedList::collect([new Foo(9), new Foo(4), new Foo(2)]),
Option::some(new Foo(2))
];
}

/**
* @param Seq<Foo> $seq
* @param Option<Foo> $option
* @dataProvider provideTestMinByNotEmptyCollections
*/
public function testMinByInNotEmptyCollection(Seq $seq, Option $option): void
{
$this->assertEquals($option, $seq->minBy(fn(Foo $foo) => $foo->a));
}

public function provideTestMinByEmptyCollections(): Generator
{
yield ArrayList::class => [
ArrayList::collect([]),
Option::none()
];
yield LinkedList::class => [
LinkedList::collect([]),
Option::none()
];
}

/**
* @param Seq<Foo> $seq
* @param Option<Foo> $option
* @dataProvider provideTestMinByEmptyCollections
*/
public function testMinByInEmptyCollection(Seq $seq, Option $option): void
{
$this->assertEquals($option, $seq->minBy(fn(Foo $foo) => $foo->a));
}
}

0 comments on commit f08c58f

Please sign in to comment.