From 53eb307fea077299d409adf3ba0307a8fda4c4d1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 19 Nov 2020 09:26:20 -0600 Subject: [PATCH] allow sorting on multiple criteria --- src/Illuminate/Collections/Arr.php | 43 +------------------ src/Illuminate/Collections/Collection.php | 50 ++++++++++++++++++++++- tests/Support/SupportArrTest.php | 6 +-- 3 files changed, 53 insertions(+), 46 deletions(-) diff --git a/src/Illuminate/Collections/Arr.php b/src/Illuminate/Collections/Arr.php index 11c53cdb0712..9d08ae2c8c24 100644 --- a/src/Illuminate/Collections/Arr.php +++ b/src/Illuminate/Collections/Arr.php @@ -604,7 +604,7 @@ public static function shuffle($array, $seed = null) * Sort the array using the given callback or "dot" notation. * * @param array $array - * @param callable|string|null $callback + * @param callable|array|string|null $callback * @return array */ public static function sort($array, $callback = null) @@ -678,45 +678,4 @@ public static function wrap($value) return is_array($value) ? $value : [$value]; } - - /** - * Sort giiven array by many properties. - * - * @param array $array - * @param array $comparisons - * @return array - */ - public static function sortByMany($array, $comparisons = []) - { - usort($array, function ($a, $b) use ($comparisons) { - foreach ($comparisons as $cmp) { - // destruct comparison array to variables - // with order set by default to 1 - [$prop, $ascending] = static::wrap($cmp) + [1 => true]; - $result = 0; - - if (is_callable($prop)) { - $result = $prop($a, $b); - } else { - $values = [static::get($a, $prop), static::get($b, $prop)]; - - if (! $ascending) { - $values = array_reverse($values); - } - - $result = $values[0] <=> $values[1]; - } - - // if result is 0, values are equal - // so we have to order items by next comparison - if ($result === 0) { - continue; - } - - return $result; - } - }); - - return $array; - } } diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 15d17ce441fe..6a9e96f4ed54 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -1110,13 +1110,17 @@ public function sortDesc($options = SORT_REGULAR) /** * Sort the collection using the given callback. * - * @param callable|string $callback + * @param callable|array|string $callback * @param int $options * @param bool $descending * @return static */ public function sortBy($callback, $options = SORT_REGULAR, $descending = false) { + if (is_array($callback)) { + return $this->sortByMany($callback); + } + $results = []; $callback = $this->valueRetriever($callback); @@ -1141,6 +1145,50 @@ public function sortBy($callback, $options = SORT_REGULAR, $descending = false) return new static($results); } + /** + * Sort the collection using multiple comparisons. + * + * @param array $comparisons + * @return static + */ + protected function sortByMany(array $comparisons = []) + { + $items = $this->items; + + usort($items, function ($a, $b) use ($comparisons) { + foreach ($comparisons as $comparison) { + $comparison = Arr::wrap($comparison); + + $prop = $comparison[0]; + + $ascending = Arr::get($comparison, 1, true) === true || + Arr::get($comparison, 1, true) === 'asc'; + + $result = 0; + + if (is_callable($prop)) { + $result = $prop($a, $b); + } else { + $values = [Arr::get($a, $prop), Arr::get($b, $prop)]; + + if (! $ascending) { + $values = array_reverse($values); + } + + $result = $values[0] <=> $values[1]; + } + + if ($result === 0) { + continue; + } + + return $result; + } + }); + + return new static($items); + } + /** * Sort the collection in descending order using the given callback. * diff --git a/tests/Support/SupportArrTest.php b/tests/Support/SupportArrTest.php index 73871670e35b..7356c4378d03 100644 --- a/tests/Support/SupportArrTest.php +++ b/tests/Support/SupportArrTest.php @@ -914,7 +914,7 @@ public function testSortByMany() ]; // sort using keys - $sorted = array_values(Arr::sortByMany($unsorted, [ + $sorted = array_values(Arr::sort($unsorted, [ 'name', 'age', 'meta.key', @@ -927,7 +927,7 @@ public function testSortByMany() ], $sorted); // sort with order - $sortedWithOrder = array_values(Arr::sortByMany($unsorted, [ + $sortedWithOrder = array_values(Arr::sort($unsorted, [ 'name', ['age', false], ['meta.key', true], @@ -940,7 +940,7 @@ public function testSortByMany() ], $sortedWithOrder); // sort using callable - $sortedWithCallable = array_values(Arr::sortByMany($unsorted, [ + $sortedWithCallable = array_values(Arr::sort($unsorted, [ function ($a, $b) { return $a['name'] <=> $b['name']; },