Reputation: 1101
I have the following multi-dimensional array that I want to sort.
I want to sort the innermost arrays by total_points, then tiebraker1, 2 and 3.
Example:
[
1 => [
1 => [
'userid' => 17,
'total_points' => 16,
'tiebraker1' => 1,
'tiebraker2' => 2,
'tiebraker3' => 1
],
2 => [
'userid' => 29,
'total_points' => 16,
'tiebraker1' => 1,
'tiebraker2' => 2,
'tiebraker3' => 9
]
],
2 => [
1 => [
'userid' => 26,
'total_points' => 26,
'tiebraker1' => 2,
'tiebraker2' => 2,
'tiebraker3' => 4
],
2 => [
'userid' => 17,
'total_points' => 26,
'tiebraker1' => 3,
'tiebraker2' => 2,
'tiebraker3' => 4
]
]
]
Desired result:
[
1 => [
1 => [
'userid' => 29,
'total_points' => 16,
'tiebraker1' => 1,
'tiebraker2' => 2,
'tiebraker3' => 9
],
2 => [
'userid' => 17,
'total_points' => 16,
'tiebraker1' => 1,
'tiebraker2' => 2,
'tiebraker3' => 1
]
],
2 => [
1 => [
'userid' => 17,
'total_points' => 26,
'tiebraker1' => 3,
'tiebraker2' => 2,
'tiebraker3' => 4
],
2 => [
'userid' => 26,
'total_points' => 26,
'tiebraker1' => 2,
'tiebraker2' => 2,
'tiebraker3' => 4
]
]
]
I tried using array_multisort but I can't configure it correctly.
Upvotes: 1
Views: 1485
Reputation: 48091
Iterate over each set of rows, preserve the keys starting from 1, sort by the last four columns, then reapply the cached keys. Demo
array_walk(
$array,
function (&$rows) {
$keys = array_keys($rows);
usort(
$rows,
fn($a, $b) => array_slice($b, 1)
<=>
array_slice($a, 1)
);
$rows = array_combine($keys, $rows);
}
);
var_export($array);
If your userid column was the last column of all rows, then rsort()
would suffice because the rows have the same element count and the column positions align with sorting priorities. Ultimately, if all scoring columns are tied, then the tie would be broken by the larger userid. Demo
array_walk(
$array,
function (&$rows) {
$keys = array_keys($rows);
rsort($rows);
$rows = array_combine($keys, $rows);
}
);
var_export($array);
Upvotes: 0
Reputation: 44289
To use array_multisort
you would need a different structure for your data. Specifically you would need to group by "score type" (or expressed mathematically, transpose the array). E.g. like this using your first example:
array(5) {
// $userid
[0] => array(2) {
[0] => 17
[1] => 29
}
// $total_points
[1] => array(2) {
[0] => 16
[1] => 16
}
// $tiebreaker1
[2] => array(4) {
[0] => 1
[1] => 1
}
// $tiebreaker2
[3] => array(2) {
[0] => 2
[1] => 2
}
// $tiebreaker3
[4] => array(2) {
[0] => 1
[1] => 9
}
}
Then you could use array_multisort()
as follows:
array_multisort($ar[1], SORT_DESC, SORT_NUMERIC,
$ar[2], SORT_DESC, SORT_NUMERIC,
$ar[3], SORT_DESC, SORT_NUMERIC,
$ar[4], SORT_DESC, SORT_NUMERIC,
$ar[0], SORT_ASC, SORT_NUMERIC);
If you cannot change the structure of the array, you could use usort()
instead and define the comparision criteria manually.
function cmp($a, $b)
{
if ($a['total_points'] != $b['total_points']) {
return ($a['total_points'] > $b['total_points']) ? -1 : 1;
} elseif ($a['tiebreaker1'] != $b['tiebreaker1']) {
return ($a['tiebreaker1'] > $b['tiebreaker1']) ? -1 : 1;
} elseif ($a['tiebreaker2'] != $b['tiebreaker2']) {
return ($a['tiebraker2'] > $b['tiebreaker2']) ? -1 : 1;
} elseif ($a['tiebreaker3'] != $b['tiebreaker3']) {
return ($a['tiebreaker3'] > $b['tiebreaker3']) ? -1 : 1;
} else {
return 0;
}
}
usort($array, "cmp");
Disclaimer: I do not claim that my implementation of cmp
is the most elegant one. But it should do the trick. :)
Upvotes: 4
Reputation: 31919
From the PHP.net's documentation:
<?php
$ar = array(
array("10", 11, 100, 100, "a"),
array( 1, 2, "2", 3, 1)
);
array_multisort($ar[0], SORT_ASC, SORT_STRING,
$ar[1], SORT_NUMERIC, SORT_DESC);
var_dump($ar);
?>
In this example, after sorting, the first array will transform to "10", 100, 100, 11, "a" (it was sorted as strings in ascending order). The second will contain 1, 3, "2", 2, 1 (sorted as numbers, in descending order).
array(2) {
[0]=> array(5) {
[0]=> string(2) "10"
[1]=> int(100)
[2]=> int(100)
[3]=> int(11)
[4]=> string(1) "a"
}
[1]=> array(5) {
[0]=> int(1)
[1]=> int(3)
[2]=> string(1) "2"
[3]=> int(2)
[4]=> int(1)
}
}
Upvotes: 0