3

I have an array of objects, which are Soccer players. The array can contain anything from zero to thousands of players. I want to reduce it to the best 10. My initial attempt was as follows :

while (count($ArrayOfPlayers) > 10) {

    $ArrayIndex = 0;
    $WorstPlayerIndex = -1;
    $WorstPlayerSkill = 9999999999;
    foreach ($ArrayOfPlayers as $Player) {
        $Skill = $Player->RatingsArray['Skill'];
        if ($Skill < $WorstPlayerSkill) {
            $WorstPlayerIndex = $ArrayIndex;
            $WorstPlayerSkill = $Skill;
        }
        $ArrayIndex += 1;
    }

    // Found the worst player in the list, so remove him.
    unset($ArrayOfPlayers[$WorstPlayerIndex]);
}

Having read through similar posts, I'm aware now that the problem is that the array isn't actually being changed, so the while loop continues forever (the computer does indeed lock).

So my attempt at correcting it is as follows, based on advice from other posts.

while (count($ArrayOfPlayers) > 10) {

        $WorstIndexPlayer = 0;
        $WorstPlayerSkill = 9999999999;
        foreach ($ArrayOfPlayers as $key => &$Player) {
            $Skill = $Player->RatingsArray['Skill'];
            if ($Skill < $WorstPlayerSkill) {
                $WorstIndexPlayer = $key;
                $WorstPlayerSkill = $Skill;
            }
        }
        // Found the worst player in the list, so remove him.
        unset($ArrayOfPlayers[$WorstIndexPlayer]);
}

As you can probably tell, I don't understand what I'm doing at this point and don't understand what the $key part is for (it's just copied from other examples). It still just hangs the PC.

How do I correct this, or is there a better way altogether to achieve this?

In response to the request for the data structure, here is a dump of just 2 players to show how they're arranged.

Array
(
[0] => Player Object
    (
        [ID] => 1
        [TeamID] => 1
        [Name] => Joseph Dorrington
        [RatingsArray] => Array
            (
                [Skill] => 51993
            )
    )

[1] => Player Object
    (
        [ID] => 2
        [TeamID] => 1
        [Name] => Oliver Tillyard
        [RatingsArray] => Array
            (
                [Skill] => 64574
            )

    )
Farflame
  • 421
  • 1
  • 5
  • 17

2 Answers2

2

Using usort, you can first sort the array by this value, and then, using array_slice, take first 10 elements:

function cmp($a, $b){
    if ($a->RatingsArray['Skill'] == $b->RatingsArray['Skill']) {
        return 0;
    }
    return ($a->RatingsArray['Skill'] > $b->RatingsArray['Skill']) ? -1 : 1;
}
usort($ArrayOfPlayers, "cmp");
$ArrayOfPlayers = array_slice($ArrayOfPlayers, 0, 10);
n-dru
  • 9,285
  • 2
  • 29
  • 42
2

I'm thinking there may be a simpler approach.

How about the approach where we sort by skill level (descending), and then "slice" the first 10 to represent the best?

Assuming your structure looks something like so:

$arrayOfPlayers = array (size=6)
0 => 
    object(stdClass)[1]
      public 'RatingsArray' => 
        array (size=1)
          'Skill' => int 1187
  1 => 
    object(stdClass)[2]
      public 'RatingsArray' => 
        array (size=1)
          'Skill' => int 44
  2 => 
    object(stdClass)[3]
      public 'RatingsArray' => 
        array (size=1)
          'Skill' => int 494
  3 => 
    object(stdClass)[4]
      public 'RatingsArray' => 
        array (size=1)
          'Skill' => int 584
  4 => 
    object(stdClass)[5]
      public 'RatingsArray' => 
        array (size=1)
          'Skill' => int 730
  5 => 
    object(stdClass)[6]
      public 'RatingsArray' => 
        array (size=1)
          'Skill' => int 613
 ...

The below code would do that for you:

// Call our custom usort function
usort($arrayOfPlayers, 'sort_players');
// Slice the array to the best 10.  Note array_slice doesn't care if there's less than 10
$best = array_slice($arrayOfPlayers, 0, 10);

// Our custom sorting function
function sort_players($a, $b) {
    if ($a->RatingsArray['Skill'] == $b->RatingsArray['Skill']) {
        return 0;
    }

    return ($a->RatingsArray['Skill'] < $b->RatingsArray['Skill']) ? 1: -1;
}
random_user_name
  • 25,694
  • 7
  • 76
  • 115
  • Thanks, you're absolutely right, this is a much better way of doing it. Processes very quickly and works perfectly. – Farflame Aug 04 '16 at 14:19
  • As people very quickly learned when they had to process data on *punched cards(!),* sorting is an "unexpectedly efficient" process. Many very-high-volume operations can be performed very efficiently (even if you're limited to punched cards or tape, as people once were) if you require that all of the streams-of-data you're working with are sorted identically. Processes that might otherwise require "indexed files" (which they didn't *have...)* can be done *sequentially* ... producing "also-still-sorted" outputs. – Mike Robinson Aug 04 '16 at 15:05
  • I rebuilt a process that used indexed files to use "pre-sorted streams" in the manner described, and it ran *three hundred times faster* than its predecessor ... *including* the sort-time! – Mike Robinson Aug 04 '16 at 15:07