Build reports — totals per category, user activity summaries, lists bucketed by status — by composing groupBy, map, and fold. Replace multi-pass code with one declarative pipeline.
A list of payment records. We want three reports from the same data:
$payments = [
['customer' => 'A', 'currency' => 'GBP', 'amount' => 100, 'status' => 'settled'],
['customer' => 'A', 'currency' => 'EUR', 'amount' => 50, 'status' => 'settled'],
['customer' => 'B', 'currency' => 'GBP', 'amount' => 75, 'status' => 'settled'],
['customer' => 'A', 'currency' => 'GBP', 'amount' => 25, 'status' => 'refunded'],
['customer' => 'B', 'currency' => 'EUR', 'amount' => 40, 'status' => 'settled'],
];use PinkCrab\FunctionConstructors\GeneralFunctions as F;
use PinkCrab\FunctionConstructors\Arrays as A;
$byCurrency = A\groupBy(F\getProperty('currency'))($payments);
// ['GBP' => [payment, payment, payment], 'EUR' => [payment, payment]]
$totalInGroup = A\sumWhere(F\getProperty('amount'));
$revenueByCurrency = array_map($totalInGroup, $byCurrency);
// ['GBP' => 200, 'EUR' => 90]groupBy buckets, sumWhere does “map to amount + sum” in one pass per bucket.
$byCustomer = A\groupBy(F\getProperty('customer'))($payments);
$statusCountsFor = fn(array $rows) =>
array_map('count', A\groupBy(F\getProperty('status'))($rows));
$summary = array_map($statusCountsFor, $byCustomer);
/*
[
'A' => ['settled' => 2, 'refunded' => 1],
'B' => ['settled' => 2],
]
*/$statusCountsFor is a reusable callable — “for a list of payments, return count per status”. Apply it to every customer’s bucket.
[$settled, $refunded] = A\partition(
fn($p) => $p['status'] === 'refunded'
)($payments);
// $settled — truthy bucket (index 1)
// $refunded — falsy bucket (index 0)Wait — partition puts truthy matches at index 1, falsy at 0. So unpacking is [$falsy, $truthy]. Rename to match:
[$notRefunded, $refunded] = A\partition(
fn($p) => $p['status'] === 'refunded'
)($payments);One traversal, two buckets.
For anything sumWhere doesn’t cover, drop to fold:
$stats = A\fold(
fn($acc, $p) => [
'count' => $acc['count'] + 1,
'total' => $acc['total'] + $p['amount'],
'max' => max($acc['max'], $p['amount']),
],
['count' => 0, 'total' => 0, 'max' => 0]
);
print_r($stats($payments));
// ['count' => 5, 'total' => 290, 'max' => 100]groupBy buckets by key.map over the buckets to summarise each.fold / sumWhere produces the summary for one bucket.partition is a special-case groupBy with a boolean key.Compose these and you’ve replaced every “two foreach loops and a running total” pattern.