diff --git a/src/Latte/Essential/CombineIterator.php b/src/Latte/Essential/CombineIterator.php new file mode 100644 index 000000000..8c60c30e0 --- /dev/null +++ b/src/Latte/Essential/CombineIterator.php @@ -0,0 +1,52 @@ +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; + } +} diff --git a/src/Latte/Essential/Filters.php b/src/Latte/Essential/Filters.php index 0705bcdf5..adf0665d8 100644 --- a/src/Latte/Essential/Filters.php +++ b/src/Latte/Essential/Filters.php @@ -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); @@ -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, + )); } diff --git a/tests/common/CombineIterator.phpt b/tests/common/CombineIterator.phpt new file mode 100644 index 000000000..74a0f7fca --- /dev/null +++ b/tests/common/CombineIterator.phpt @@ -0,0 +1,54 @@ + $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), + ); +}); diff --git a/tests/filters/group.phpt b/tests/filters/group.phpt index ac86d3685..8aeb34e61 100644 --- a/tests/filters/group.phpt +++ b/tests/filters/group.phpt @@ -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]; } @@ -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]]]], diff --git a/tests/filters/sort.phpt b/tests/filters/sort.phpt index 99d62cd6f..0301cd094 100644 --- a/tests/filters/sort.phpt +++ b/tests/filters/sort.phpt @@ -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],