Reputation: 4807
This is for tie-break situations ranking players in a series. As such, ignore the totals of the scores, they'll be the same (1400 in this example).
So, given the following array:
array(2) {
array(3) {
[0] => array(1) {
["score"] => 500
}
[1] => array(1) {
["score"] => 500
}
[2] => array(1) {
["score"] => 400
}
}
array(4) {
[0] => array(1) {
["score"] => 600
}
[1] => array(1) {
["score"] => 600
}
[2] => array(1) {
["score"] => 100
}
[3] => array(1) {
["score"] => 100
}
}
}
I'd like to sort it so that the subarray with the highest individual score (or NUMBER of highest scores) comes out on top. If that's stiil a tie, we work down to their second highest scores, etc.
E.g. in the above example, 600,600,100,100
is better than 500,500,400
, even though they both total 1400.
Similarly 0,100,300,300
would be better than 300,200,200,0,0
0,100,100,50
would be better than 100,100,25,0,0,25
, etc.
This is the best I've come up with so far, and it seems very messy to me, I feel there must be better/cleaner options:
function sortTies($a, $b)
{
// get arrays of the scores and their counts
// each array item will have the score as the key and a count as the value
$aa = array_count_values(array_column($a, 'score'));
$bb = array_count_values(array_column($b, 'score'));
// sort them so that the highest scores are first
krsort($aa);
krsort($bb);
// get the length of the longest array
$maxLength = max(count($aa), count($bb));
// now we loop through, comparing the $i'th index of $aa to that of $bb
for($i = 0; $i < $maxLength; $i++) {
reset($aa);
reset($bb);
// move $i items deeper into the arrays
for($n=0; $n < $i; $n++) {
next($aa);
next($bb);
}
// if the keys differ at a certain position, we have our sort criteria, so should return...
if (key($aa) !== key($bb)) {
return key($bb) <=> key($aa)
}
// ...otherwise, we check the value stored under those keys in each array
elseif (current($aa) !== current($bb)) {
return current($bb) <=> current($aa);
}
// if the key and the value are the same, we don't return and we carry on into the next
// iteration of the loop, going one element deeper in each array
}
return 0; //dead heat
}
What could I do to make this better?
Upvotes: 2
Views: 149
Reputation: 452
Loop through the list, group by score, sum quantity ( instance of score ). Order by score desc. Get a factor Score * Quantity, then construct an array with the factor and the list. Finally, sort desc by factor.
Like this:
<?php
$data=array(
array(
array(
"score" => 500
),
array(
"score" => 500
),
array(
"score" => 400
)
),
array(
array(
"score" => 600
),
array(
"score" => 600
),
array(
"score" => 100
),
array(
"score" => 100
)
)
);
$result = array();
foreach( $data as $i => $list ) {
$group = array();
foreach( $list as $index => $item ) {
if ( empty( $group ) ) {
$group[$item["score"]] = 1;
} else {
if ( isset( $group[$item["score"]] ) ) {
$qty = $group[$item["score"]];
$qty = $qty + 1;
$group[$item["score"]] = $qty;
} else {
$group[$item["score"]] = 1;
} // end if isset score
} // end if then else empty
} // end foreach $list
if ( !empty( $group ) ) {
// Order by key, desc
krsort( $group );
// Get the factor
$key = key( $group );
$factor = $key * $group[$key];
// Push
$result[$factor] = $list;
} // end if group not empty
} // end foreach $data
krsort( $result );
print_r( $result );
Upvotes: 1