Skip to content

Commit 89c3b46

Browse files
authored
Merge pull request #46 from mavlitov98/group-map-reduce
Add groupMapReduce
2 parents 5241648 + 967946c commit 89c3b46

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"src/Fp/Functions/Collection/Fold.php",
6161
"src/Fp/Functions/Collection/ForAll.php",
6262
"src/Fp/Functions/Collection/GroupBy.php",
63+
"src/Fp/Functions/Collection/GroupMapReduce.php",
6364
"src/Fp/Functions/Collection/Head.php",
6465
"src/Fp/Functions/Collection/Keys.php",
6566
"src/Fp/Functions/Collection/Last.php",
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Fp\Collection;
6+
7+
/**
8+
* Partitions this iterable collection into a map according to a discriminator function key.
9+
* All the values that have the same discriminator are then transformed by the value function and
10+
* then reduced into a single value with the reduce function.
11+
*
12+
* ```php
13+
* >>> groupMapReduce(
14+
* collection: [
15+
* ['id' => 10, 'val' => 10],
16+
* ['id' => 10, 'val' => 15],
17+
* ['id' => 10, 'val' => 20],
18+
* ['id' => 20, 'val' => 10],
19+
* ['id' => 20, 'val' => 15],
20+
* ['id' => 30, 'val' => 20],
21+
* ],
22+
* group: fn(array $a) => $a['id'],
23+
* map: fn(array $a) => [$a['val']],
24+
* reduce: fn(array $old, array $new) => array_merge($old, $new),
25+
* );
26+
* => [10 => [10, 15, 20], 20 => [10, 15], 30 => [20]]
27+
* ```
28+
*
29+
* @template K of array-key
30+
* @template A
31+
* @template KOut of array-key
32+
* @template B
33+
*
34+
* @param iterable<K, A> $collection
35+
* @param callable(A): KOut $group
36+
* @param callable(A): B $map
37+
* @param callable(B, B): B $reduce
38+
* @return array<KOut, B>
39+
*
40+
* @psalm-return ($collection is non-empty-array
41+
* ? non-empty-array<KOut, B>
42+
* : array<KOut, B>)
43+
*/
44+
function groupMapReduce(iterable $collection, callable $group, callable $map, callable $reduce): array
45+
{
46+
$grouped = [];
47+
48+
foreach ($collection as $item) {
49+
$key = $group($item);
50+
51+
if (array_key_exists($key, $grouped)) {
52+
$grouped[$key] = $reduce($grouped[$key], $map($item));
53+
} else {
54+
$grouped[$key] = $map($item);
55+
}
56+
}
57+
58+
return $grouped;
59+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Runtime\Functions\Collection;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use function Fp\Collection\groupBy;
9+
use function Fp\Collection\groupMapReduce;
10+
11+
final class GroupMapReduceTest extends TestCase
12+
{
13+
public function testGroup(): void
14+
{
15+
$this->assertEquals(
16+
[
17+
10 => [10, 15, 20],
18+
20 => [10, 15],
19+
30 => [20],
20+
],
21+
groupMapReduce(
22+
[
23+
['id' => 10, 'sum' => 10],
24+
['id' => 10, 'sum' => 15],
25+
['id' => 10, 'sum' => 20],
26+
['id' => 20, 'sum' => 10],
27+
['id' => 20, 'sum' => 15],
28+
['id' => 30, 'sum' => 20],
29+
],
30+
/** @param array{id: int, sum: int} $a */
31+
fn(array $a) => $a['id'],
32+
/** @param array{id: int, sum: int} $a */
33+
fn(array $a) => [$a['sum']],
34+
/**
35+
* @param list<int> $old
36+
* @param list<int> $new
37+
*/
38+
fn(array $old, array $new) => array_merge($old, $new),
39+
)
40+
);
41+
}
42+
}

0 commit comments

Comments
 (0)