Skip to content

Commit

Permalink
allow sorting on multiple criteria
Browse files Browse the repository at this point in the history
  • Loading branch information
taylorotwell committed Nov 19, 2020
1 parent 174c0e5 commit 53eb307
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 46 deletions.
43 changes: 1 addition & 42 deletions src/Illuminate/Collections/Arr.php
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
}
50 changes: 49 additions & 1 deletion src/Illuminate/Collections/Collection.php
Expand Up @@ -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);
Expand All @@ -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.
*
Expand Down
6 changes: 3 additions & 3 deletions tests/Support/SupportArrTest.php
Expand Up @@ -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',
Expand All @@ -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],
Expand All @@ -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'];
},
Expand Down

0 comments on commit 53eb307

Please sign in to comment.