diff --git a/src/Collection.php b/src/Collection.php index 7bd40be..e13fb14 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -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 = []; @@ -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 */ 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); } @@ -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); @@ -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) { diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index 5177379..59f95be 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -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); + } }