• ベストアンサー

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

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

  • Perl
  • 回答数6
  • ありがとう数6

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

  • ベストアンサー
回答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にリファレンスを入れることもやってみたのですが、うまくいきませんでした。 きっと私のプログラムが間違っているのだと思います。 もう一度よく確認してみます。

関連するQ&A

  • 【javascript】ハッシュのキーをソートして取り出したい

    ハッシュのキーをソートして取り出したいです。 perlで表現すると、以下の様なかんじです。 foreach $key (sort keys %hash) { ... } そこで、prototype.jsを使って以下の様に書いてみました。 <html> <head> <script type="text/javascript" src="prototype.js"></script> </head> <body > <script > var table = {c:'C', b:'B', a:'A'}; $H(table).keys().sort().each(function(key){ alert(key + ' ' + table[key]); }); </script> </body> </html> 他に良い方法ありましたら、教えてください。

  • 静的ハッシュの配列のキーに対応する値の数の多さ順で表示させたい

    ハッシュのキーに対応する値の数の多さ順で表示させたいと考え、下記の所まで試行錯誤しておりますが、どうにも思ったようにソートできずにおります。 #!/usr/bin/perl use strict; my(%a, $i, $j ,$allarray ,@keys ,@keys2 ,%hash ,%files ,$a_mumei_ref ,$key ,$value ,@value ,$x ,$files); # ハッシュの配列を静的に作る %a = ( '0' => [ qw(0) ], '1' => [ qw(1 1) ], '3' => [ qw(3 3 3) ], '7' => [ qw(7 7 7) ], '2' => [ qw(2) ], '4' => [ qw() ], '5' => [ qw() ], '6' => [ qw() ], '8' => [ qw(8 8) ], '9' => [ qw(9) ], ); @keys = sort { $hash{$b} <=> $hash{$a} || length($b) <=> length($a) || $a cmp $b } keys %a; #ハッシュのキーを数字順で表示 foreach (@keys){ print $_ ."\n"; } # 静的に作ったハッシュの配列を取り出してみる foreach $i (sort keys %a) { for ($j = 0; $j <= scalar(@{$a{$i}})-1; $j++) { print '$a{'. $i. '}['. $j. ']='. $a{$i}[$j]. ' '; } $allarray=scalar(@{$a{$i}})-1; print "No$i:kosuu:$allarray"; print "\n"; #配列の値の個数を調べその配列を作成 my($a_mumei) = $allarray; $a_mumei_ref = \$a_mumei; $files{"$i"}=($i,$a_mumei_ref); } #each関数で%filesの中身を表示 while ( ( $key , $value ) = each %files ){ print "key:$key value:$$value\n" ; } #試行錯誤 foreach $x (sort { $files{$b} <=> $files{$a} } keys %files){ print "$x => $files->{$x}\n"; } @keys2 = sort {$hash{$a} <=> $hash{$b}} keys %files; #@keys2 = sort { $hash{$b} <=> $hash{$a} || length($b) <=> length($a) || $a cmp $b } keys %files; #@keys2 = sort { $hash{$a} cmp $hash{$b} } keys %files; print "@keys2\n"; print "\n"; __END__; 私のイメージしておりますのは、ソートした結果がハッシュのキーに対応する値の数の多さ順で下記のように表示させたいのですが、 どのようにすれば可能でございますか、ご教授願えませんでしょうか key:3 value:2・・・この場合valueは配列の個数 key:7 value:2 key:8 value:1 key:1 value:1 key:9 value:0 key:2 value:0 key:0 value:0 key:6 value:-1 key:4 value:-1 key:5 value:-1

    • ベストアンサー
    • Perl
  • perl ハッシュの重複について、よくわかっていないので教えていただき

    perl ハッシュの重複について、よくわかっていないので教えていただきたいです。。 例えば、product_id (ユニーク)、product_name、purchased_price という内容のデータを保存しようとして、このような形のものがあるとします ↓ $product{product_id}->{product_name}->{purchased_price} product_id と product_name の2つのセットには重複がないのですが、その先の、purchased_price は、1つの商品につきいくつもありえるので、このなかで重複が出てきます。 これを、同じ product_id/product_name に対して出現した purchased_price の合計を求めて1つのバリューとしたいのですが、どのようにすればよいのでしょうか。。。。 例: product_id, product_name, purchased_price {1}->{'test'}->{100} {1}->{'test'}->{200} {1}->{'test'}->{300} {1}->{'test'}->{100} とあったとしたら、これを全部足して、 {1}->{'test'}->{700} のようにしたいのですが・・・・・・・ できるのでしょうか。。。??

  • ハッシュのソート

    ハッシュに以下のようなデータが格納されている場合 valueでソートして表示するにはどうしたらよいでしょうか? 教えてください。 %hoge = (a => 10, b => 3, c => 7); 表示 a => 10 c => 7 b => 3

    • ベストアンサー
    • Perl
  • 【初歩的質問】重複データがある時のハッシュへの代入について

    perl5.8です。すごくしようもない質問で申し訳ないのですが、次のようなファイルfile.txtの内容を、ハッシュ%hashに入れていくとします。 --- file.txtの中身 --- a,1 c,3 a,1 b,2 c,3 ----------------------- --- ソース(抜粋) ----- open(IN, "file.txt"); @data = <IN>; close(IN); %hash = (); foreach(@data){ chomp $_; @out = split(/,/, $_); $hash{$out[0]} = $out[1]; } ----------------------- 上記の結果は当然ながら、$hash{a}=1,$hash{c}=3,$hash{b}=2となるのですが、重複したデータを読み込んでハッシュに入れようとした時に、ワーニングなりエラーがなにも出なかったのがちょっと気持ち悪いです。重複したキーを読み込んだ時は、内部的には黙ってはじいてくれていると解釈してよいのでしょうか?そうだとすると、こういう書き方は、重複した行を排除するテクニックとなりえるのでしょうか?

  • Ruby 2次元のハッシュ

    Rubyで2次元のハッシュを扱いたいです。 perlで書くと以下のような感じです。(最近perlに疎遠なので自信無いですが^^;) hash{$key1}{$key2}=$value; foreach $key1 (keys %hash){ foreach $key2 (keys %{$hash{$key1}}){ print "$hash{$key1}{$key2}\n"; } } Rubyだとどんな感じになりますか?

  • ハッシュの中身の表示

    ハッシュの中身の確認ができなくて困っています。 下記のような実行文においてです。 当然、test の戻り値は、スカラーとハッシュです。ハッシュを戻すときには参照渡し記号の\もつけています。 my ($return_code, %hash_data) = test(); 表示しようとすると、 Hash(0x5b04) のような表示にしかなりません、、 (試した表示方法は、下記4つです。) (環境は、WindowsXP上での、ActivePerl-5.10.0.1004 です。) foreach $key ( keys( %Hash ) ) { print "キー値 : $key\n"; print "値 : $Hash{$key} \n " } while ( ( $key , $value ) = each %Hash ){ print "キー値 : $key\n"; print "値 : $value \n " ; } use Data::Dump qw(dump); print dump(\%hash); #print %display_test; 宜しくお願いします。

    • ベストアンサー
    • Perl
  • (Perl)ReadParseによるハッシュへの格納について

    PerlのReadPerseについて質問させてください。 以下少々長いですが、過程を書きます。 <form>タグにより、入力フォームのページをHTMLで作成したとします(form.html)。 その際、<form method="post" action="form.cgi"> と入力しておいて、別途form.cgiを作成するとします。 form.htmlには、<form>タグ内のname属性で、それぞれ'weight' 'height' 'age'という三種類のデータを入力してもらって、取得するようにしています。 そして<input type="submit" value="登録">による、 登録ボタンを押すことで、 form.cgiに処理をさせるようにしています。 form.cgiの中身ですが、まず、最初の三行を書きます。 #!/usr/local/bin/perl require 'cgi-lib.pl'; &ReadParse(*form); となるのですが、わからないのが、 三行目の &ReadParse(*form); です。 サブルーチンReadParseを引数*formで、 cgi-lib.plから呼び出しているということはわかるのですが、いきなり*formという形で、ハッシュを登場させているあたりがわかりません。(この際、ハッシュ名の前の%を*に変えなければならないことは、一応知っております。理由はわかりませんが。) そこで質問なのですが、 いきなり、ハッシュであるformを登場させていいものなのでしょうか?? 以下、form.cgiの内容です。 #!/usr/local/bin/perl require 'cgi-lib.pl'; &ReadParse(*form); $name=$form{'weight'}; $sex=$form{'height'}; $megane=$form{'age'}; print "Content-type: text/html\n\n"; print "<html>\n"; print "<body>\n"; print "体重:${weight}<br>\n"; print "身長:${height}<br>\n"; print "年齢:${age}<br>\n"; print "</body>"; print "</html>"; _END_

    • ベストアンサー
    • CGI
  • phpで重複チェック

    phpの重複チェック phpで配列の重複データをチェックして、重複しているデータを表示しようとしています。 foreach ($arry as $key => $value) { $err_count = 0; foreach ($arry as $key => $value2) { if ($value == $value2) { $err_count++; if ($err_count >= 2) { echo "重複".$value2; } } } } としているのですが、 同じモノが2回表示されて困っています。 重複しているデータは一回だけ表示したいのですが、 なにか方法ないでしょうか?

    • ベストアンサー
    • PHP
  • ハッシュをkeysでうまく抽出できない

    ハッシュのキーのリストを取ってきたいのですが、期待通りの動きをしてくれません。 いろいろ試したところ、以下のようなコードを作成した時、エラーになるようです。この場合、son, daughter, papi, mamaだけを出力したいのに、uncleが混じってきます。 正しいキーを抽出するには、どうしたら良いのでしょうか? perlのバージョンは、5.8.8です。 my %family = ( son => {age => 27, sex => man, height => 175}, daughter => {age => 20, sex => woman, height => 160}, papi => {age => 60, sex => man, height => 170}, mama => {age => 57, sex => woman, height => 161}, ); if(exists $family{'uncle'}->{age}){ } foreach my $key(keys %family){ printf "%s\n", $key; }

    • ベストアンサー
    • Perl