将一个对象数组简化为“最佳10个”。

3

我有一个对象数组,这些对象是足球运动员。该数组可能包含从零到数千名球员。我想将其减少到最好的10个。我的初始尝试如下:

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]);
}

阅读了类似的帖子后,我现在意识到问题在于数组实际上没有被更改,因此 while 循环会无限继续(计算机确实会锁定)。

因此,根据其他帖子的建议,我尝试进行以下更正。

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]);
}

您可能已经注意到,我现在不明白自己在做什么,也不理解 $key 部分的作用(这只是从其他示例中复制的)。它仍然会让电脑卡住。

我该如何更正这一点,或者有没有更好的方法来实现这一点?

针对数据结构的请求,以下是仅显示 2 个玩家如何排列的转储。

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
            )

    )

请问您能否发布数据结构? - Kisaragi
更新了问题,展示了数据结构。希望这就是您想要的? - Farflame
2个回答

2

使用usort函数,您可以首先按照此值对数组进行排序,然后使用array_slice函数取前10个元素:

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);

1
哇,这简单多了,而且第一次就成功了。速度也非常快。谢谢 :) - Farflame

2

我认为可能有一种更简单的方法。

我们可以按技能水平(降序)排序,然后“切片”前10个来代表最好的。

假设您的结构类似于这样:

$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
 ...

以下代码可以实现你所需的功能:
// 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;
}

谢谢,你说得完全正确,这是更好的方法。处理速度非常快,而且完美无缺。 - Farflame
当人们必须在打孔卡片上处理数据时,他们很快就学会了排序是一个“出乎意料的高效”过程。许多大容量操作可以非常高效地执行(即使你受限于打孔卡或磁带,就像人们曾经那样),如果您要求您正在使用的所有数据流都被相同地排序。否则可能需要“索引文件”的进程(它们不存在...)可以按顺序完成...产生“也仍然排序”的输出。 - Mike Robinson
我重新构建了一个使用索引文件的进程,以所描述的方式使用“预排序流”,它的运行速度比其前身快了三百倍...包括排序时间! - Mike Robinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接