ランキング
前回の「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; }