Skip to content

Commit

Permalink
added filter |group
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Apr 30, 2024
1 parent 1537c6e commit a9a8521
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/Latte/Essential/CoreExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ public function getFilters(): array
? [$this->filters, 'firstUpper']
: fn() => throw new RuntimeException('Filter |firstUpper requires mbstring extension.'),
'floor' => [$this->filters, 'floor'],
'group' => [$this->filters, 'group'],
'implode' => [$this->filters, 'implode'],
'indent' => [$this->filters, 'indent'],
'join' => [$this->filters, 'implode'],
Expand Down
32 changes: 32 additions & 0 deletions src/Latte/Essential/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,38 @@ public static function sort(iterable $iterable, ?\Closure $comparison = null): i
}


/**
* Groups elements by the element indices and preserves the key association and order.
*/
public static function group(iterable $iterable, string|int|\Closure $by): \Generator
{
$fn = $by instanceof \Closure ? $by : fn($a) => is_array($a) ? $a[$by] : $a->$by;
$keys = $groups = $prevKey = [];

foreach ($iterable as $k => $v) {
$groupKey = $fn($v, $k);
if (!$groups || $prevKey !== $groupKey) {
$index = array_search($groupKey, $keys, true);
if ($index === false) {
$index = count($keys);
$keys[$index] = $groupKey;
}
$prevKey = $groupKey;
}
$groups[$index][0][] = $k;
$groups[$index][1][] = $v;
}

foreach ($groups as $index => $pair) {
yield $keys[$index] => (static function () use ($pair): \Generator {
foreach ($pair[1] as $i => $value) {
yield $pair[0][$i] => $value;
}
})();
}
}


/**
* Returns value clamped to the inclusive range of min and max.
*/
Expand Down
88 changes: 88 additions & 0 deletions tests/filters/group.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

/**
* Test: Latte\Essential\Filters::group()
*/

declare(strict_types=1);

use Latte\Essential\Filters;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


function iterator(): Generator
{
yield ['a' => 55] => ['k' => 22, 'k2'];
yield ['a' => 66] => (object) ['k' => 22, 'k2'];
yield ['a' => 77] => ['k' => 11];
yield ['a' => 88] => ['k' => 33];
}


function exportIterator(Traversable $iterator): array
{
$res = [];
foreach ($iterator as $key => $value) {
$res[] = [$key, $value instanceof Traversable ? exportIterator($value) : $value];
}
return $res;
}


test('array', function () {
Assert::equal(
[
[22, [
[0, ['k' => 22, 'k2']],
[1, (object) ['k' => 22, 'k2']],
]],
[11, [[2, ['k' => 11]]]],
[33, [[3, ['k' => 33]]]],
],
exportIterator(Filters::group(
[['k' => 22, 'k2'], (object) ['k' => 22, 'k2'], ['k' => 11], ['k' => 33]],
'k',
)),
);
Assert::same([], exportIterator(Filters::group([], 'k')));
});


test('iterator', function () {
Assert::equal(
[
[22, [
[['a' => 55], ['k' => 22, 'k2']],
[['a' => 66], (object) ['k' => 22, 'k2']],
]],
[11, [[['a' => 77], ['k' => 11]]]],
[33, [[['a' => 88], ['k' => 33]]]],
],
exportIterator(Filters::group(iterator(), 'k')),
);
});


test('array + callback', function () {
Assert::same(
[[220, [[0, 22]]], [110, [[1, 11]]], [330, [[2, 33]]]],
exportIterator(Filters::group([22, 11, 33], fn($a) => $a * 10)),
);
});


test('iterator + callback', function () {
Assert::equal(
[
[-22, [
[['a' => 55], ['k' => 22, 'k2']],
[['a' => 66], (object) ['k' => 22, 'k2']],
]],
[-11, [[['a' => 77], ['k' => 11]]]],
[-33, [[['a' => 88], ['k' => 33]]]],
],
exportIterator(Filters::group(iterator(), fn($a) => -((array) $a)['k'])),
);
});

0 comments on commit a9a8521

Please sign in to comment.