【问题标题】:Building a "crosstab" or "pivot" table from an array in php从 php 中的数组构建“交叉表”或“数据透视表”
【发布时间】:2010-10-17 06:22:36
【问题描述】:

我有一个与下面类似的对象数组:

$scores = array();

// Bob round 1
$s = new RoundScore();
$s->Round_Name = 'Round 1';
$s->Player_Name = 'Bob';
$s->Score = 10;
$scores[0] = $s;

// Bob round 2
$s = new RoundScore();
$s->Round_Name = 'Round 2';
$s->Player_Name = 'Bob';
$s->Score = 7;
$scores[1] = $s;

// Jack round 1
$s = new RoundScore();
$s->Round_Name = 'Round 1';
$s->Player_Name = 'Jack';
$s->Score = 6;
$scores[2] = $s;

// Jack round 2
$s = new RoundScore();
$s->Round_Name = 'Round 2';
$s->Player_Name = 'Jack';
$s->Score = 12;
$scores[3] = $s;

如果我循环遍历 $scores 对象并将其转储到表中,它将如下所示:

Round_Name 玩家得分 ---------------------------- 第 1 轮鲍勃 10 第 2 轮鲍勃 7 第一轮杰克 6 回合 2 杰克 12

然而,我想要的是这样的:

玩家 第 1 轮 第 2 轮总计 ------------------------------------------- 鲍勃 10 7 17 杰克 6 12 18

我不会提前知道会有多少轮或多少玩家,我只能说我无法改变对象的构造方式。

在 php 中最有效的方法是什么?

【问题讨论】:

  • 我认为你有一个错字。对于 Bob 第 2 轮,您有:“$rounds[1] = $s;”代替:“$scores[1] = $s;”
  • 谢谢,你是对的 - 我现在已经修好了 :)

标签: php arrays performance pivot-table


【解决方案1】:

据我所知,PHP 数组是作为哈希表实现的(因此查找/更新应该非常高效) 无论如何,时间效率是否会成为问题?

我会以“简单”的方式来做:

$table = array();
$round_names = array();
$total = array();

foreach ($scores as $score)
{
    $round_names[] = $score->Round_Name;
    $table[$score->Player_Name][$score->Round_Name] = $score->score;
    $total[$score->Player_Name] += $score->score;
}

$round_names = array_unique($round_names);

foreach ($table as $player => $rounds)
{
    echo "$player\t";
    foreach ($round_names as $round)
        echo "$rounds[$round]\t";
    echo "$total[$player]\n";
}

(我知道数组没有正确初始化,但你明白了)

【讨论】:

  • 我赞成这个答案而不是 maxyfc 的,因为它更简单、更健壮。我真的不认为性能是这里的瓶颈。代码的可维护性更重要。你可能还想做一个natsort($round_names) btw。
  • 为什么你使用 foreach ($round_names as $round) echo rounds[$round]\t"; 而不是 foreach ($rounds as $round) echo $round
【解决方案2】:

如果我们可以假设:

  • 数组中的分数顺序始终按照玩家姓名的顺序排列 然后按整数
  • 每个玩家的回合数相同

然后,我们可以做的是打印每个玩家的得分,因为我们在数组中移动,同时计算过程中的总分,但如果我们看到新玩家则重置它:

$round_count = 0;
$header_printed = false;
$current_player = NULL;
$current_total = 0;
$current_output_line = "";
foreach ($scores as $score) {
    // Check whether we have to move to a new player
    if ($score->Player_Name != $current_player) {
        // Check whether we have anything to print before
        // resetting the variables
        if (!is_null($current_player)) {
            if (!$header_printed) {
                printf("%-10s", "Player");
                for ($i = 0; $i < $round_count; $i++) {
                    printf("%-10s", "Round $i");
                }
                printf("%-10s\n", "Total");

                $header_printed = true;
            }

            $current_output_line .= sprintf("%5d\n", $current_total);
            print $current_output_line;
        }

        // Reset the total and various variables for the new player
        $round_count = 0;
        $current_player = $score->Player_Name;
        $current_total = 0;
        $current_output_line = sprintf("%-10s", $score->Player_Name);
    }

    $round_count++;
    $current_total += $score->Score;
    $current_output_line .= sprintf("%5d     ", $score->Score);
}
// The last player is not printed because we exited the loop 
// before the print statement, so we need a print statement here.
if ($current_output_line != "") {
    $current_output_line .= sprintf("%5d\n", $current_total);
    print $current_output_line;
}

样本输出:

Player    Round 0   Round 1   Total
Bob          10         7        17
Jack          6        12        18

这应该是相当有效的,因为它只通过数组一次。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-06
    • 1970-01-01
    • 1970-01-01
    • 2020-05-24
    • 1970-01-01
    • 2012-04-07
    相关资源
    最近更新 更多