電子書籍の厳選無料作品が豊富!

現在、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でこの重複を回避する方法を教えていただきたく思います。

A 回答 (6件)

どうしてもハッシュを使わなくていいなら、データを配列にして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];
になります、たぶん。
    • good
    • 0
この回答へのお礼

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

お礼日時:2007/10/20 01:53

ANo.5さんへ。


ANo.3です。

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

すみません、そういう事(上)です。
余計な事書いて間違えてました。
    • good
    • 0
この回答へのお礼

返事が遅れてしまい申し訳ありません。
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
確かにこのことは参考書などにも記載されておりますが、私は何となくしかその意味を理解できていないんですよね。
要勉強です。

お礼日時:2007/10/21 18:19

たぶんハッシュの要素に配列のリファレンスを入れるところで


詰まっていると思うので参考にでも。
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 みたいなもの
なのであまり気にすることはないです。
    • good
    • 0
この回答へのお礼

返事が遅れてしまい申し訳ありません。
なるほど、理解できました。
分かり易い説明、ありがとうございます。

>if (!defined $data{$height}) {
>$data{$height} = [];
>}
>push @{$data{$height}}, $weight;

ここがキーポイントですね。

お礼日時:2007/10/21 18:36

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
    • good
    • 0
この回答へのお礼

お返事が遅れ大変申し訳ありません。
通し番号を追加させるというのは思いつきませんでした。
ありがとうございます。

皆さんのご意見を参考に、無事に解決することができました。
本当にありがとうございました。

お礼日時:2007/10/23 00:37

私は素人(完全独学)なので、本当に正しいかわからないのですが・・・


(間違ってたら、誰か指摘してください)

$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";}
    • good
    • 0

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

    • good
    • 0
この回答へのお礼

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

お礼日時:2007/10/20 00:10

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


おすすめ情報