• ベストアンサー

ハッシュで重複キーを認める方法について

現在、Perlを用いてBootstrap法という方法論によるデータの加工を行っています。 その中でハッシュを使います。 それはkeyでsortしvalueを並び替え、その並び替えたvalueを処理することが目的です。  例えばこのようなものです。  Height  Weight   170.6  54.8   185.7  87.2   156.1  78.6   185.7  87.2   164.5  54.7   156.1  45.3    :     : 以上のようなデータに対し、Heightをkeyにsortし、Weightを並び替え、その並び替えたWeightの値を上から順に同数ずつ、複数のグループに分類することが目的です。 ですがハッシュでは重複keyはvalueが上書きされてしまうので、元のデータより少なくなってしまい正確なsort、並び替えができません。 Perlでこの重複を回避する方法を教えていただきたく思います。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

どうしてもハッシュを使わなくていいなら、データを配列にしてsortするという手もありますが・・・ my @data=( [170.6 , 54.8], [185.7 , 87.2], [185.7 , 87.2], [185.7 , 87.2], [185.7 , 87.2], [156.1 , 78.6], [185.7 , 87.2], [164.5 , 54.7], [156.1 , 45.3]); @data = @data[sort {$data[$a][0] <=> $data[$b][0]} 0..$#data]; for my $i(0..$#data){ print "$data[$i][0] , $data[$i][1]<br>\n"; } p.s. @heightと@weightに分かれていて、@heightをkeyにして@weightをsortするなら、 @weight = @weight[sort {$height[$a] <=> $height[$b]} 0..$#height]; になります、たぶん。

tabibito2
質問者

お礼

なるほど、こんな方法があるのですね。 sort {$data[$a][0] <=> $data[$b][0]} このような表記でsortできるのは違和感があるのですが、まだ私が『{$a<=>$b}』の意味を十分に理解していないのと、『0..$#height』という記述の仕方で、$dataを@data($data[$i])として扱えるということかと解しました。 早速試してみます。 ありがとうございました。

その他の回答 (5)

回答No.6

ANo.5さんへ。 ANo.3です。 >あと回答欄で間違いがあったら指摘してくれということなので。 > >> $a <=> $bは、「$aと$bを数値として比べてね(昇順で)」という意味です。 >カッコ内が余計です。この比較自体にはソートを昇順でするか降順でするか >の意味はありません。あくまで $a と $b を比較して >$a > $ b のとき -1 >$a == $b のとき 0 >$a < $b のとき 1 >を返しているだけです。 すみません、そういう事(上)です。 余計な事書いて間違えてました。

tabibito2
質問者

お礼

返事が遅れてしまい申し訳ありません。 ANo.6さんはAno.3さんと同じ方のようなので、失礼とは思いますが併せてお礼申し上げます。 >@data = @data[sort {$data[$a][0] <=> $data[$b][0]} 0..$#data]; >の、0..$#dataは、配列の添え字です。 >わかりやすく言えば、(0,1,2,3,4,5,6,7,8)を作っただけです。 非常に分かり易い説明、ありがとうございます。 なるほど、そういうことだったのですね。 納得です。 >$a > $ b のとき -1 >$a == $b のとき 0 >$a < $b のとき 1 確かにこのことは参考書などにも記載されておりますが、私は何となくしかその意味を理解できていないんですよね。 要勉強です。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.5

たぶんハッシュの要素に配列のリファレンスを入れるところで 詰まっていると思うので参考にでも。 use strict; use warnings; my %data; while (my $entry = <DATA>) { chomp $entry; my ($height, $weight) = split q{,}, $entry; if (!defined $data{$height}) { $data{$height} = []; } push @{$data{$height}}, $weight; } foreach my $key (sort {$a <=> $b} keys %data) { print $key, " : ", join(q{,}, @{$data{$key}}), "\n"; } __DATA__ 170.6,54.8 165.7,87.2 156.1,78.6 185.7,87.2 164.5,54.7 156.1,45.3 実行結果: 156.1 : 78.6,45.3 164.5 : 54.7 165.7 : 87.2 170.6 : 54.8 185.7 : 87.2 あと回答欄で間違いがあったら指摘してくれということなので。 > $a <=> $bは、「$aと$bを数値として比べてね(昇順で)」という意味です。 カッコ内が余計です。この比較自体にはソートを昇順でするか降順でするか の意味はありません。あくまで $a と $b を比較して $a > $ b のとき -1 $a == $b のとき 0 $a < $b のとき 1 を返しているだけです。 a とか b という名前は placeholder みたいなもの なのであまり気にすることはないです。

tabibito2
質問者

お礼

返事が遅れてしまい申し訳ありません。 なるほど、理解できました。 分かり易い説明、ありがとうございます。 >if (!defined $data{$height}) { >$data{$height} = []; >} >push @{$data{$height}}, $weight; ここがキーポイントですね。

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.4

key に通し番号を付けてはどうでしょうか? use strict; my $no = 0; my %hash; while (<DATA>) { $no++; my ($key, $value) = split /\s+/; $hash{"${key}_$no"} = $value; } foreach my $key (sort { substr($a, 0, 5) <=> substr($b, 0, 5) or $hash{$a} <=> $hash{$b} } keys %hash) { print substr($key, 0, 5), " $hash{$key}\n"; } __DATA__ 170.6 54.8 165.7 87.2 156.1 78.6 185.7 87.2 164.5 54.7 156.1 45.3

tabibito2
質問者

お礼

お返事が遅れ大変申し訳ありません。 通し番号を追加させるというのは思いつきませんでした。 ありがとうございます。 皆さんのご意見を参考に、無事に解決することができました。 本当にありがとうございました。

回答No.3

私は素人(完全独学)なので、本当に正しいかわからないのですが・・・ (間違ってたら、誰か指摘してください) $a <=> $b は、「$aと$bを数値として比べてね(昇順で)」という意味です。 では、$aと$b は何かと言うと、sortの作業中のある2つのデータです。 なぜa,bと言う名前なのかについては、特に意味は無いと思いますが・・・ で、 @data = @data[sort {$data[$a][0] <=> $data[$b][0]} 0..$#data]; の、0..$#dataは、配列の添え字です。 わかりやすく言えば、(0,1,2,3,4,5,6,7,8)を作っただけです。 そして、0,1,2,3,4,5,6,7,8を並べ替えて、新たな添え字を作っているのです。 例えば4と5を比べる時は、$data[4][0]と$data[5][0]を数値として比べてねと言う意味で、{$data[$a][0] <=> $data[$b][0]}と指示しています。 ちなみに、2番目のデータ(weight)をキーとして並び替えるなら、{$data[$a][1] <=> $data[$b][1]}です。 ここの指定しだいで、複数キーなど、複雑なsortをすることもできます。 たとえば、1番目のデータ(height)で並べ替えるんだけど、1番目のデータが同じ場合は2番目のデータ(weight)の降順でと言うなら、{$data[$a][0] <=> $data[$b][0] or $data[$b][1] <=> $data[$a][2]}になります、多分。 言葉で言いにくいので、以下を見てください。 実データを並び替えずに、並び替えられた添え字の配列を作る事ができます。 my @data=( [170.6 , 54.8], [185.7 , 87.2], [185.7 , 87.2], [185.7 , 87.2], [185.7 , 87.2], [156.1 , 78.6], [185.7 , 87.2], [164.5 , 54.7], [156.1 , 45.3]); print "content-type: text/html\n\n"; #最初のデータの状態 for my $i(0..$#data){print "$i : $data[$i][0] , $data[$i][1]<br>\n";}print "<br>\n"; #データ分の添え字の配列を作る my @n=(0..$#data); #上はmy @n=(0,1,2,3,4,5,6,7,8);と同じ意味 #最初の添え字の状態 for my $i(0..$#n){print "\$n[$i]=$n[$i]<br>\n";}print "<br>\n"; #0から8までの数値の配列を並び替える #ただし、並び替えるルールが@dataの内容に依存する @n=sort{$data[$a][0]<=>$data[$b][0]} @n; #上は@n=sort{$data[$a][0]<=>$data[$b][0]} 0..$#n;と同じ #並び替えられた添え字の状態 for my $i(0..$#n){print "\$n[$i]=$n[$i]<br>\n";}print "<br>\n"; #並び替えられた添え字順のデータ for my $i(0..$#n){print "$data[$n[$i]][0] , $data[$n[$i]][1]<br>\n";}

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

ハッシュに配列のリファレンスを入れる.

tabibito2
質問者

お礼

ありがとうございます。 一応、valueにリファレンスを入れることもやってみたのですが、うまくいきませんでした。 きっと私のプログラムが間違っているのだと思います。 もう一度よく確認してみます。