ランキング

前回の「sort」を発展させて、ランキングを作ってみましょう。

my %hash = (A => 2, B => 5, C => 3, D => 5);

こんな配列があるとします。これをどういう風にしたいかというと、


1位 A 5点
2位 B 3点(同位)
2位 C 3点(同位)
4位 D 2点

とこんな感じにしてみましょう。

ではまず、これを点数が多い順に並べ替えたいですよね。
5点、3点、3点、2点と言った具合に。
ハッシュで言うと$list{B},$list{D},$list{C},$list{A]といった感じです。
ハッシュや配列は、その都度順番がかわります。なので、同値のBとDは
前後がかわったりもします。それもきちんと並べる方法もありますが、
今回は同位ならどちらでもよいということにします。

まず、全体の流れはこれ。

my @list;#セットしたハッシュを入れる配列
my $rank = 1;#順位
my $last_rank = 1; #前回の順位
my $last_pt = 0; # 前回の点数 
my $counter = 1;#カウンター
foreach my $key(sort { $hash{$b} <=> $hash{$a} } keys %hash) {
my %list;

#今回のポイント
my $pt = $hash{$key};

#同点なら前回のランクを入れる
if($pt < $last_pt) {
$rank = $counter;
} else {
#点数が違うならカウンターを入れる
$rank = $last_rank;
}
#ハッシュにそれぞれの値をセット
$list{key} = $key;
$list{value} = $hash{$key};
$list{rank} = $rank;

#配列に格納
push(@list,\%list);
	
	
$counter++; #全体の回数
$last_pt = $pt; #前回の点数
$last_rank = $rank; #前回の順位

}

では、ハッシュをキー順にソートしてみましょう。

foreach my $key(sort { $hash{$b} <=> $hash{$a} } keys %hash) {

はい、できました。
そして、それぞれの値をまた別のハッシュ値に入れます。
これはHTML::Templateモジュールを使用するためです。

my %list; #ハッシュ値を入れる配列を宣言

$list{key} = $key;#キー
$list{value} = $hash{$key};#値
$list{rank} = $rank;#順位
push(@list,¥%list);#ハッシュの参照を配列に入れる

$list{key}にA,B,C,Dを、$list{value}に5点、3点、2点。。を。
そして、$list{rank}に順位をセットします。

ひとまずこれでソートすることができました。
順番になっていると思います。
続いて$rankに順位を設定します。
順位で悩む点は、同じ点数の場合は同じ順位にし、さらに
2位が2人いたら、次の人は3位ではなく4位にします。

#点数が違うならカウンターを入れる
if($pt < $last_pt) {
$rank = $counter;
} else {
#点数が同じなら前回と同じ数字を入れる
$rank = $last_rank;
}

$ptは今回の点数、$last_ptは前回の点数で、
今回が前回と同じ点数であれば$rank(順位)に前回と同じ順位を入れます。
次に、点数が前回よりも小さければ、カウンターを入れます。
カウンター($counter)というのは、
foreachでループしている間の数を数えている変数です。
直前の点数と、今回の点数を比較して順位を決めて行きます。


ポイントは、前回と比較をするために、前回の点数や前回の順位を
次のループにおくってやること。
これがわからなくてかなり苦労しました;;

MEMO

my %hash;
#重複データを数える
foreach my $name(@namelist) {
	$hash{$name}++;
}

my @list;
my $rank = 1;#順位
my $last_rank = 1; #前回の順位
my $last_pt = 0; # 直前のポイント 
my $counter = 1;
foreach my $key(sort { $hash{$b} <=> $hash{$a} } keys %hash) {
	my %list;
	#今回のポイント
	my $pt = $hash{$key};
	#同点なら前回のランクを入れる
	if($pt < $last_pt) {
		$rank = $counter;
	} else {
	#点数が違うならカウンターを入れる
		$rank = $last_rank;
	}
	$list{key} = $key;
	$list{value} = $hash{$key};
	$list{rank} = $rank;

	push(@list,\%list);
	
	#全体の回数
	$counter++;
	#直前のポイントを更新
	$last_pt = $pt;
	#直前のランキングを更新
	$last_rank = $rank;
}