将对象数组减少到“最佳 10 个”

Posted

技术标签:

【中文标题】将对象数组减少到“最佳 10 个”【英文标题】:Reduce an array of objects to the 'best 10' 【发布时间】:2016-12-10 16:59:09 【问题描述】:

我有一个对象数组,它们是足球运动员。该数组可以包含从零到数千名玩家的任何内容。我想把它减少到最好的 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 部分的用途(它只是从其他示例中复制的)。它仍然只是挂起 PC。

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

为响应对数据结构的要求,这里仅列出 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
            )

    )

【问题讨论】:

能把数据结构发一下吗 更新问题,显示数据结构。希望这就是你想要的? 【参考方案1】:

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

【讨论】:

哇,这太简单了,而且第一次就奏效了。也非常快。谢谢:)【参考方案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;

【讨论】:

谢谢,您说的完全正确,这是一种更好的方法。处理速度非常快,效果很好。 当人们不得不处理穿孔卡片(!)上的数据时,他们很快就知道,排序是一个“出乎意料的高效”过程。如果您需要对您正在处理的所有数据流进行相同的排序,则可以非常有效地执行许多非常大容量的操作(即使您仅限于穿孔卡片或磁带,就像人们曾经那样) .原本可能需要“索引文件”(他们没有拥有...)的进程可以按顺序完成...产生“也仍然排序”的输出. 我重建了一个进程,该进程使用索引文件以所述方式使用“预排序流”,它的运行速度比其前身快 三百倍 ... 包括排序时间!

以上是关于将对象数组减少到“最佳 10 个”的主要内容,如果未能解决你的问题,请参考以下文章

将对象数组减少为对象[重复]

我将如何减少这个数组,以便每个对象中的对象被合并 javascript 但不知道键值名称

如何将数组内的对象和每个对象相乘,获取一个或多个键并减少它们的值?

JS:将数组减少为嵌套对象

在JS中将对象数组减少为hashmap

无法根据字符串值减少对象数组