rana_kualuさんの記事を読んでたら、自分でもできそうな問題があったので挑戦。
PHPは長らく書いてなかったので、練習にはちょうど良い問題だった。
top10ranking.php
<?php
$file = $argv[1];
// 集計
foreach(csv($file) as [, $id, $score]){
$player[$id] ??= ['total'=>0, 'count'=>0, 'id'=>$id];
$player[$id]['total'] += $score;
$player[$id]['count'] += 1;
}
// 平均点
foreach($player as $id => $v){
$player[$id]['mean'] = round($v['total'] / $v['count']);
}
// 平均点とIDでソート
array_multisort(array_column($player, 'mean'), SORT_DESC, array_column($player, 'id'), SORT_ASC, $player);
// 順位
$all = 1;
$prev = null;
foreach($player as $id => $v){
if($v['mean'] !== $prev){
$rank = $all;
}
$all++;
$prev = $v['mean'];
$player[$id]['rank'] = $rank;
}
// 出力
print "rank,player_id,mean_score\n";
foreach($player as $id => $v){
if($v['rank'] > 10){
break;
}
printf("%s,%s,%s\n", $v['rank'], $id, $v['mean']);
}
// CSVジェネレータ
function csv($file){
$fp = fopen($file, 'r');
fgetcsv($fp);
while($line = fgetcsv($fp)){
yield $line;
}
}
最初は出力と同時に順位の計算を行っていたが、複雑になったので分離。
順位を計算するコードは書いたことがなかったので、検索して参考にした。
全プレイヤーの順位を計算してるのは無駄だが、最初はこんなものである。
それでもやはり、順位を求めるコードが汚いのはどうにかしたい所。
関数化するのが良いのかなあ?
ソート済みの配列を渡すと、順位を返すジェネレータ
function ranking(array $sorted){
foreach($sorted as $i => $v){
if($i === 0 or $v !== $sorted[$i-1]){
$rank = $i + 1;
}
yield $rank;
}
}