Skip to content

Commit

Permalink
filters |sort & |group returns CombineIterator
Browse files Browse the repository at this point in the history
allows multiple iterations
  • Loading branch information
dg committed May 2, 2024
1 parent 9753ccf commit 6824d11
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 21 deletions.
52 changes: 52 additions & 0 deletions src/Latte/Essential/CombineIterator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

/**
* This file is part of the Latte (https://latte.nette.org)
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);

namespace Latte\Essential;


/**
* @internal
*/
class CombineIterator implements \Iterator
{
public function __construct(
private array $pairs,
) {
}


public function current(): mixed
{
return current($this->pairs)[1];
}


public function key(): mixed
{
return current($this->pairs)[0];
}


public function next(): void
{
next($this->pairs);
}


public function rewind(): void
{
reset($this->pairs);
}


public function valid(): bool
{
return key($this->pairs) !== null;
}
}
32 changes: 12 additions & 20 deletions src/Latte/Essential/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -458,28 +458,23 @@ public static function sort(iterable $iterable, ?\Closure $comparison = null): i
return $iterable;
}

$keys = $values = [];
$pairs = [];
foreach ($iterable as $key => $value) {
$keys[] = $key;
$values[] = $value;
$pairs[] = [$key, $value];
}
$comparison ? uasort($values, $comparison) : asort($values);
uasort($pairs, fn($a, $b) => $comparison ? $comparison($a[1], $b[1]) : $a[1] <=> $b[1]);

return (static function () use ($keys, $values): \Generator {
foreach ($values as $i => $value) {
yield $keys[$i] => $value;
}
})();
return new CombineIterator($pairs);
}


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

foreach ($iterable as $k => $v) {
$groupKey = $fn($v, $k);
Expand All @@ -491,17 +486,14 @@ public static function group(iterable $iterable, string|int|\Closure $by): \Gene
}
$prevKey = $groupKey;
}
$groups[$index][0][] = $k;
$groups[$index][1][] = $v;
$groups[$index][] = [$k, $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;
}
})();
}
return new CombineIterator(array_map(
fn($key, $group) => [$key, new CombineIterator($group)],
$keys,
$groups,
));
}


Expand Down
54 changes: 54 additions & 0 deletions tests/common/CombineIterator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

/**
* Test: CombineIterator basic usage.
*/

declare(strict_types=1);

use Latte\Essential\CombineIterator;
use Tester\Assert;

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


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


test('empty array', function () {
Assert::same(
[],
exportIterator(new CombineIterator([], [])),
);
});


test('single usage', function () {
$arr = [[new stdClass, new stdClass], [null, null]];
$iterator = new CombineIterator(array_column($arr, 0), array_column($arr, 1));
Assert::same(
$arr,
exportIterator($iterator),
);
});


test('double usage', function () {
$arr = [[new stdClass, new stdClass], [null, null]];
$iterator = new CombineIterator(array_column($arr, 0), array_column($arr, 1));
Assert::same(
$arr,
exportIterator($iterator),
);
Assert::same(
$arr,
exportIterator($iterator),
);
});
31 changes: 30 additions & 1 deletion tests/filters/group.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ 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' => 66] => (object) ['k' => 22, 'k2'];
yield ['a' => 88] => ['k' => 33];
}

Expand Down Expand Up @@ -65,6 +65,35 @@ test('iterator', function () {
});


test('multiple calls', function () {
$groups = Filters::group(iterator(), 'k');

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($groups),
);

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($groups),
);
});


test('array + callback', function () {
Assert::same(
[[220, [[0, 22]]], [110, [[1, 11]]], [330, [[2, 33]]]],
Expand Down
15 changes: 15 additions & 0 deletions tests/filters/sort.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ test('iterator', function () {
});


test('multiple calls', function () {
$sorted = Filters::sort(iterator());

Assert::same(
[['b', 10], ['a', 20], [[true], 30]],
exportIterator($sorted),
);

Assert::same(
[['b', 10], ['a', 20], [[true], 30]],
exportIterator($sorted),
);
});


test('user comparison + array', function () {
Assert::same(
[2 => 30, 0 => 20, 1 => 10],
Expand Down

0 comments on commit 6824d11

Please sign in to comment.