Skip to content

Commit

Permalink
Merge pull request #46 from mavlitov98/group-map-reduce
Browse files Browse the repository at this point in the history
Add groupMapReduce
  • Loading branch information
klimick authored Jun 15, 2022
2 parents 5241648 + 967946c commit 89c3b46
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 0 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"src/Fp/Functions/Collection/Fold.php",
"src/Fp/Functions/Collection/ForAll.php",
"src/Fp/Functions/Collection/GroupBy.php",
"src/Fp/Functions/Collection/GroupMapReduce.php",
"src/Fp/Functions/Collection/Head.php",
"src/Fp/Functions/Collection/Keys.php",
"src/Fp/Functions/Collection/Last.php",
Expand Down
59 changes: 59 additions & 0 deletions src/Fp/Functions/Collection/GroupMapReduce.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace Fp\Collection;

/**
* Partitions this iterable collection into a map according to a discriminator function key.
* All the values that have the same discriminator are then transformed by the value function and
* then reduced into a single value with the reduce function.
*
* ```php
* >>> groupMapReduce(
* collection: [
* ['id' => 10, 'val' => 10],
* ['id' => 10, 'val' => 15],
* ['id' => 10, 'val' => 20],
* ['id' => 20, 'val' => 10],
* ['id' => 20, 'val' => 15],
* ['id' => 30, 'val' => 20],
* ],
* group: fn(array $a) => $a['id'],
* map: fn(array $a) => [$a['val']],
* reduce: fn(array $old, array $new) => array_merge($old, $new),
* );
* => [10 => [10, 15, 20], 20 => [10, 15], 30 => [20]]
* ```
*
* @template K of array-key
* @template A
* @template KOut of array-key
* @template B
*
* @param iterable<K, A> $collection
* @param callable(A): KOut $group
* @param callable(A): B $map
* @param callable(B, B): B $reduce
* @return array<KOut, B>
*
* @psalm-return ($collection is non-empty-array
* ? non-empty-array<KOut, B>
* : array<KOut, B>)
*/
function groupMapReduce(iterable $collection, callable $group, callable $map, callable $reduce): array
{
$grouped = [];

foreach ($collection as $item) {
$key = $group($item);

if (array_key_exists($key, $grouped)) {
$grouped[$key] = $reduce($grouped[$key], $map($item));
} else {
$grouped[$key] = $map($item);
}
}

return $grouped;
}
42 changes: 42 additions & 0 deletions tests/Runtime/Functions/Collection/GroupMapReduceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Tests\Runtime\Functions\Collection;

use PHPUnit\Framework\TestCase;
use function Fp\Collection\groupBy;
use function Fp\Collection\groupMapReduce;

final class GroupMapReduceTest extends TestCase
{
public function testGroup(): void
{
$this->assertEquals(
[
10 => [10, 15, 20],
20 => [10, 15],
30 => [20],
],
groupMapReduce(
[
['id' => 10, 'sum' => 10],
['id' => 10, 'sum' => 15],
['id' => 10, 'sum' => 20],
['id' => 20, 'sum' => 10],
['id' => 20, 'sum' => 15],
['id' => 30, 'sum' => 20],
],
/** @param array{id: int, sum: int} $a */
fn(array $a) => $a['id'],
/** @param array{id: int, sum: int} $a */
fn(array $a) => [$a['sum']],
/**
* @param list<int> $old
* @param list<int> $new
*/
fn(array $old, array $new) => array_merge($old, $new),
)
);
}
}

0 comments on commit 89c3b46

Please sign in to comment.