Skip to content

Commit

Permalink
Fix sortByMany return is not same to sortBy & Perf sortBy (#6925)
Browse files Browse the repository at this point in the history
  • Loading branch information
szutoutou authored Jul 4, 2024
1 parent 95c956c commit 8d9cd79
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 5 deletions.
34 changes: 29 additions & 5 deletions src/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -1088,7 +1088,7 @@ public function sort(?callable $callback = null): static
public function sortBy($callback, int $options = SORT_REGULAR, bool $descending = false): static
{
if (is_array($callback) && ! is_callable($callback)) {
return $this->sortByMany($callback);
return $this->sortByMany($callback, $options);
}

$results = [];
Expand All @@ -1112,11 +1112,21 @@ public function sortBy($callback, int $options = SORT_REGULAR, bool $descending
/**
* Sort the collection in descending order using the given callback.
*
* @param (callable(TValue, TKey): mixed)|string $callback
* @param array|(callable(TValue, TKey): mixed)|string $callback
* @return static<TKey, TValue>
*/
public function sortByDesc($callback, int $options = SORT_REGULAR): static
{
if (is_array($callback) && ! is_callable($callback)) {
foreach ($callback as $index => $key) {
$comparison = Arr::wrap($key);

$comparison[1] = 'desc';

$callback[$index] = $comparison;
}
}

return $this->sortBy($callback, $options, true);
}

Expand Down Expand Up @@ -1589,11 +1599,11 @@ public function countBy($countBy = null)
*
* @return static
*/
protected function sortByMany(array $comparisons = [])
protected function sortByMany(array $comparisons = [], int $options = SORT_REGULAR)
{
$items = $this->items;

usort($items, function ($a, $b) use ($comparisons) {
uasort($items, function ($a, $b) use ($comparisons, $options) {
foreach ($comparisons as $comparison) {
$comparison = Arr::wrap($comparison);

Expand All @@ -1611,7 +1621,21 @@ protected function sortByMany(array $comparisons = [])
$values = array_reverse($values);
}

$result = $values[0] <=> $values[1];
if (($options & SORT_FLAG_CASE) === SORT_FLAG_CASE) {
if (($options & SORT_NATURAL) === SORT_NATURAL) {
$result = strnatcasecmp($values[0], $values[1]);
} else {
$result = strcasecmp($values[0], $values[1]);
}
} else {
$result = match ($options) {
SORT_NUMERIC => intval($values[0]) <=> intval($values[1]),
SORT_STRING => strcmp($values[0], $values[1]),
SORT_NATURAL => strnatcmp($values[0], $values[1]),
SORT_LOCALE_STRING => strcoll($values[0], $values[1]),
default => $values[0] <=> $values[1],
};
}
}

if ($result === 0) {
Expand Down
181 changes: 181 additions & 0 deletions tests/CollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1100,4 +1100,185 @@ public function testAfterReturnsNullWhenItemOnTheLastItem($collection)
$c = new $collection(['foo' => 'bar', 1, 2, 3, 4, 5]);
$this->assertNull($c->after(5));
}

#[DataProvider('collectionClassProvider')]
public function testSortBy($collection)
{
$data = (new $collection(
[
['id' => 5, 'name' => 'e'],
['id' => 4, 'name' => 'd'],
['id' => 3, 'name' => 'c'],
['id' => 2, 'name' => 'b'],
['id' => 1, 'name' => 'a'],
]
))->sortBy('id');
$this->assertEquals(json_encode([
4 => ['id' => 1, 'name' => 'a'],
3 => ['id' => 2, 'name' => 'b'],
2 => ['id' => 3, 'name' => 'c'],
1 => ['id' => 4, 'name' => 'd'],
0 => ['id' => 5, 'name' => 'e'],
]), (string) $data);
$this->assertEquals(json_encode([
['id' => 1, 'name' => 'a'],
['id' => 2, 'name' => 'b'],
['id' => 3, 'name' => 'c'],
['id' => 4, 'name' => 'd'],
['id' => 5, 'name' => 'e'],
]), (string) $data->values());
$dataMany = (new $collection(
[
['id' => 5, 'name' => 'e'],
['id' => 4, 'name' => 'd'],
['id' => 3, 'name' => 'c'],
['id' => 2, 'name' => 'b'],
['id' => 1, 'name' => 'a'],
]
))->sortBy(['id', 'asc']);
$this->assertEquals((string) $data, (string) $dataMany);

$data = (new $collection(
[
['id' => 5, 'name' => '5a'],
['id' => 4, 'name' => '4b'],
['id' => 3, 'name' => 'c3'],
['id' => 2, 'name' => '2d'],
['id' => 1, 'name' => '1e'],
]
))->sortBy('name', SORT_NUMERIC);
$this->assertEquals(json_encode([
2 => ['id' => 3, 'name' => 'c3'],
4 => ['id' => 1, 'name' => '1e'],
3 => ['id' => 2, 'name' => '2d'],
1 => ['id' => 4, 'name' => '4b'],
0 => ['id' => 5, 'name' => '5a'],
]), (string) $data);
$dataMany = (new $collection(
[
['id' => 5, 'name' => '5a'],
['id' => 4, 'name' => '4b'],
['id' => 3, 'name' => 'c3'],
['id' => 2, 'name' => '2d'],
['id' => 1, 'name' => '1e'],
]
))->sortBy([['name', 'asc']], SORT_NUMERIC);
$this->assertEquals((string) $data, (string) $dataMany);

$data = (new $collection(
[
['id' => 5, 'name' => '5a'],
['id' => 4, 'name' => '4b'],
['id' => 3, 'name' => 'c3'],
['id' => 2, 'name' => '2d'],
['id' => 1, 'name' => '1e'],
]
))->sortBy('name', SORT_STRING);
$this->assertEquals(json_encode([
4 => ['id' => 1, 'name' => '1e'],
3 => ['id' => 2, 'name' => '2d'],
1 => ['id' => 4, 'name' => '4b'],
0 => ['id' => 5, 'name' => '5a'],
2 => ['id' => 3, 'name' => 'c3'],
]), (string) $data);
$dataMany = (new $collection(
[
['id' => 5, 'name' => '5a'],
['id' => 4, 'name' => '4b'],
['id' => 3, 'name' => 'c3'],
['id' => 2, 'name' => '2d'],
['id' => 1, 'name' => '1e'],
]
))->sortBy([['name', 'asc']], SORT_STRING);
$this->assertEquals((string) $data, (string) $dataMany);

$data = (new $collection(
[
['id' => 5, 'name' => 'a10'],
['id' => 4, 'name' => 'a4'],
['id' => 3, 'name' => 'a3'],
['id' => 2, 'name' => 'a2'],
['id' => 1, 'name' => 'a1'],
]
))->sortBy('name', SORT_NATURAL);
$this->assertEquals(json_encode([
4 => ['id' => 1, 'name' => 'a1'],
3 => ['id' => 2, 'name' => 'a2'],
2 => ['id' => 3, 'name' => 'a3'],
1 => ['id' => 4, 'name' => 'a4'],
0 => ['id' => 5, 'name' => 'a10'],
]), (string) $data);
$dataMany = (new $collection(
[
['id' => 5, 'name' => 'a10'],
['id' => 4, 'name' => 'a4'],
['id' => 3, 'name' => 'a3'],
['id' => 2, 'name' => 'a2'],
['id' => 1, 'name' => 'a1'],
]
))->sortBy([['name', 'asc']], SORT_NATURAL);
$this->assertEquals((string) $data, (string) $dataMany);

setlocale(LC_COLLATE, 'en_US.utf8');
$data = (new $collection(
[
['id' => 5, 'name' => 'A'],
['id' => 4, 'name' => 'a'],
['id' => 3, 'name' => 'B'],
['id' => 2, 'name' => 'b'],
['id' => 1, 'name' => 'c'],
]
))->sortBy('name', SORT_LOCALE_STRING);
$this->assertEquals(json_encode([
1 => ['id' => 4, 'name' => 'a'],
0 => ['id' => 5, 'name' => 'A'],
3 => ['id' => 2, 'name' => 'b'],
2 => ['id' => 3, 'name' => 'B'],
4 => ['id' => 1, 'name' => 'c'],
]), (string) $data);
$dataMany = (new $collection(
[
['id' => 5, 'name' => 'A'],
['id' => 4, 'name' => 'a'],
['id' => 3, 'name' => 'B'],
['id' => 2, 'name' => 'b'],
['id' => 1, 'name' => 'c'],
]
))->sortBy([['name', 'asc']], SORT_LOCALE_STRING);
$this->assertEquals((string) $data, (string) $dataMany);

$data = (new $collection(
[
['id' => 1, 'name' => 'a'],
['id' => 2, 'name' => 'b'],
['id' => 3, 'name' => 'c'],
['id' => 4, 'name' => 'd'],
['id' => 5, 'name' => 'e'],
]
))->sortByDesc('id');
$this->assertEquals(json_encode([
4 => ['id' => 5, 'name' => 'e'],
3 => ['id' => 4, 'name' => 'd'],
2 => ['id' => 3, 'name' => 'c'],
1 => ['id' => 2, 'name' => 'b'],
0 => ['id' => 1, 'name' => 'a'],
]), (string) $data);
$this->assertEquals(json_encode([
['id' => 5, 'name' => 'e'],
['id' => 4, 'name' => 'd'],
['id' => 3, 'name' => 'c'],
['id' => 2, 'name' => 'b'],
['id' => 1, 'name' => 'a'],
]), (string) $data->values());
$dataMany = (new $collection(
[
['id' => 1, 'name' => 'a'],
['id' => 2, 'name' => 'b'],
['id' => 3, 'name' => 'c'],
['id' => 4, 'name' => 'd'],
['id' => 5, 'name' => 'e'],
]
))->sortByDesc(['id']);
$this->assertEquals((string) $data, (string) $dataMany);
}
}

0 comments on commit 8d9cd79

Please sign in to comment.