Codemonkey
Codemonkey

Reputation: 4807

How to sort tie-breakers based on highest scores and quantity thereof in nested arrays?

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

Answers (1)

espino316
espino316

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

Related Questions