diff --git a/src/Latte/Essential/Filters.php b/src/Latte/Essential/Filters.php index c5d34589d..254daad67 100644 --- a/src/Latte/Essential/Filters.php +++ b/src/Latte/Essential/Filters.php @@ -451,10 +451,22 @@ public static function batch(iterable $list, int $length, $rest = null): \Genera /** * Sorts elements using the comparison function and preserves the key association. */ - public static function sort(iterable $iterable, ?\Closure $comparison = null): iterable - { + public static function sort( + iterable $iterable, + ?\Closure $comparison = null, + bool $byKey = false, + string|int|\Closure|null $by = null, + ): array|KeyValueIterator + { + $comparison ??= fn($a, $b) => $a <=> $b; + $comparison = match (true) { + $by === null => $comparison, + $by instanceof \Closure => fn($a, $b) => $comparison($by($a), $by($b)), + default => fn($a, $b) => $comparison(is_array($a) ? $a[$by] : $a->$by, is_array($b) ? $b[$by] : $b->$by), + }; + if (is_array($iterable)) { - $comparison ? uasort($iterable, $comparison) : asort($iterable); + $byKey ? uksort($iterable, $comparison) : uasort($iterable, $comparison); return $iterable; } @@ -462,7 +474,7 @@ public static function sort(iterable $iterable, ?\Closure $comparison = null): i foreach ($iterable as $key => $value) { $pairs[] = [$key, $value]; } - uasort($pairs, fn($a, $b) => $comparison ? $comparison($a[1], $b[1]) : $a[1] <=> $b[1]); + uasort($pairs, fn($a, $b) => $byKey ? $comparison($a[0], $b[0]) : $comparison($a[1], $b[1])); return new KeyValueIterator($pairs); } diff --git a/tests/filters/sort.phpt b/tests/filters/sort.phpt index 2916ede74..e93c0ae00 100644 --- a/tests/filters/sort.phpt +++ b/tests/filters/sort.phpt @@ -14,9 +14,9 @@ require __DIR__ . '/../bootstrap.php'; function iterator(): Generator { - yield 'a' => 20; - yield 'b' => 10; - yield [true] => 30; + yield ['a' => 55] => ['k' => 22]; + yield ['a' => 66] => (object) ['k' => 11]; + yield ['a' => 77] => ['k' => 33]; } @@ -31,29 +31,39 @@ function exportIterator(Traversable $iterator): array test('array', function () { - Assert::same([1 => 10, 0 => 20, 30], Filters::sort([20, 10, 30])); + Assert::same([1 => 11, 0 => 22, 33], Filters::sort([22, 11, 33])); Assert::same([], Filters::sort([])); }); test('iterator', function () { - Assert::same( - [['b', 10], ['a', 20], [[true], 30]], - exportIterator(Filters::sort(iterator())), + $sorted = Filters::sort(iterator()); + + Assert::same(3, count($sorted)); + Assert::equal( + [ + [['a' => 55], ['k' => 22]], + [['a' => 77], ['k' => 33]], + [['a' => 66], (object) ['k' => 11]], + ], + exportIterator($sorted), ); }); test('re-iteration', function () { $sorted = Filters::sort(iterator()); - - Assert::same( - [['b', 10], ['a', 20], [[true], 30]], + $res = [ + [['a' => 55], ['k' => 22]], + [['a' => 77], ['k' => 33]], + [['a' => 66], (object) ['k' => 11]], + ]; + Assert::equal( + $res, exportIterator($sorted), ); - - Assert::same( - [['b', 10], ['a', 20], [[true], 30]], + Assert::equal( + $res, exportIterator($sorted), ); }); @@ -61,15 +71,122 @@ test('re-iteration', function () { test('user comparison + array', function () { Assert::same( - [2 => 30, 0 => 20, 1 => 10], - Filters::sort([20, 10, 30], fn($a, $b) => $b <=> $a) + [2 => 33, 0 => 22, 1 => 11], + Filters::sort([22, 11, 33], fn($a, $b) => $b <=> $a) ); }); test('user comparison + iterator', function () { - Assert::same( - [[[true], 30], ['a', 20], ['b', 10]], + Assert::equal( + [ + [['a' => 66], (object) ['k' => 11]], + [['a' => 77], ['k' => 33]], + [['a' => 55], ['k' => 22]], + ], exportIterator(Filters::sort(iterator(), fn($a, $b) => $b <=> $a)), ); }); + + +test('array + by', function () { + Assert::equal( + [1 => (object) ['k' => 11], 0 => ['k' => 22], ['k' => 33]], + Filters::sort([['k' => 22], (object) ['k' => 11], ['k' => 33]], by: 'k'), + ); + Assert::same([], Filters::sort([], by: 'k')); +}); + + +test('iterator + by', function () { + Assert::equal( + [ + [['a' => 66], (object) ['k' => 11]], + [['a' => 55], ['k' => 22]], + [['a' => 77], ['k' => 33]], + ], + exportIterator(Filters::sort(iterator(), by: 'k')), + ); +}); + + +test('callback + array + by', function () { + Assert::same( + [1 => 11, 0 => 22, 33], + Filters::sort([22, 11, 33], by: fn($a) => $a * 11) + ); +}); + + +test('callback + iterator + by', function () { + Assert::equal( + [ + [['a' => 77], ['k' => 33]], + [['a' => 55], ['k' => 22]], + [['a' => 66], (object) ['k' => 11]], + ], + exportIterator(Filters::sort(iterator(), by: fn($a) => -((array) $a)['k'])), + ); +}); + + +test('array + byKey', function () { + Assert::same([1 => 11, 0 => 22, 33], Filters::sort([22, 11, 33])); + Assert::same([], Filters::sort([], byKey: true)); +}); + + +test('iterator + byKey', function () { + Assert::equal( + [ + [['a' => 55], ['k' => 22]], + [['a' => 66], (object) ['k' => 11]], + [['a' => 77], ['k' => 33]], + ], + exportIterator(Filters::sort(iterator(), byKey: true)), + ); +}); + + +test('user comparison + array + byKey', function () { + Assert::same( + [2 => 33, 1 => 11, 0 => 22], + Filters::sort([22, 11, 33], fn($a, $b) => $b <=> $a, byKey: true), + ); +}); + + +test('user comparison + iterator + byKey', function () { + Assert::equal( + [ + [['a' => 77], ['k' => 33]], + [['a' => 66], (object) ['k' => 11]], + [['a' => 55], ['k' => 22]], + ], + exportIterator(Filters::sort(iterator(), fn($a, $b) => $b <=> $a, byKey: true)), + ); +}); + + +test('iterator + by + byKey', function () { + Assert::equal( + [ + [['a' => 55], ['k' => 22]], + [['a' => 66], (object) ['k' => 11]], + [['a' => 77], ['k' => 33]], + ], + exportIterator(Filters::sort(iterator(), byKey: true, by: 'a')), + ); +}); + + +test('callback + iterator + by + byKey', function () { + Assert::equal( + [ + [['a' => 77], ['k' => 33]], + [['a' => 66], (object) ['k' => 11]], + [['a' => 55], ['k' => 22]], + ], + exportIterator(Filters::sort(iterator(), byKey: true, by: fn($a) => -((array) $a)['a'])), + ); +});