Skip to content

Commit

Permalink
filter |sort: added by & byKey
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed May 3, 2024
1 parent 70a3541 commit f1fb20d
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 21 deletions.
20 changes: 16 additions & 4 deletions src/Latte/Essential/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -451,18 +451,30 @@ 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;
}

$pairs = [];
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);
}
Expand Down
151 changes: 134 additions & 17 deletions tests/filters/sort.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}


Expand All @@ -31,45 +31,162 @@ 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),
);
});


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'])),
);
});

0 comments on commit f1fb20d

Please sign in to comment.