ハッシュに追加した順番通りに取り出す - Perl

通常、ハッシュに入れた値をkeysなどを使って取り出すと、配列のように追加した順番通りに並ぶことはまずないといってもよいし、期待してはならない。この問題を解決するには、つまりハッシュに順番を保持させる簡単な方法は「Tie::IxHash」モジュールを使用することだろう。

Tie::IxHash」モジュールは、最初にtieで関連づけさせれば、後は通常通りkeysなどで操作すればよい。解除したくなったらuntieでOKだ。

サンプル

;#
;#順番通りにハッシュから取り出す
;#

use Tie::IxHash;

#---------------------------------------#
#準備する
#---------------------------------------#
#-- ハッシュとの関連づけ --#
tie %hash, 'Tie::IxHash';

#-- ハッシュに値を代入 --#
%hash = (
      'a' => 1
    , 'b' => 2
    , 'c' => 3
    , 'd' => 4
    , 'e' => 5
);


#---------------------------------------#
#表示
#---------------------------------------#
foreach my $key ( keys %hash ){
    print "$key,$hash{$key}\n";
}

実行結果

a,1
b,2
c,3
d,4
e,5

「Tie::IxHash」モジュールは標準ではインストールされない。CPANからダウンロード、インストールする必要がある。 なお、このモジュールはPerlのみで書かれている(PurePerlの)ため、解凍後、ファイルを適当なディレクトリに置いて呼び出せば特に問題なく使用できる。

もし、本当に「Tie::IxHash」モジュールが効いているか疑うのなら、use, tieの行をコメントアウトして実行してみればよい。

注意点

使用する際に次の点に注意する必要がある。

関連づけの前に入れた値は無効

当たり前の話ではありますが、tieした以降に代入された値にのみ有効。tieした瞬間にハッシュは真っ白な状態に。最初に入れたデータはuntieすれば関連付け前の値が取り出せるようになる。

use Tie::IxHash;

#-- ハッシュに値を代入 --#
%hash = (
      'a' => 1
    , 'b' => 2
    , 'c' => 3
);

#-- ハッシュとの関連づけ --#
tie %hash, 'Tie::IxHash';

無名ハッシュには使えない(ハズ)

次のようなハッシュを組んだ場合、一番最初のキーにはIxHashが効くが、それ以降の無名ハッシュ部分には効かない。以下の例だと、'muscat', 'apple', 'banana'の順番は保証されるが、それ以降の'price','santi','omosa'については出鱈目(でたらめ)な順番で取り出される。

use Tie::IxHash;

#-- ハッシュとの関連づけ --#
tie %hash, 'Tie::IxHash';

#-- ハッシュに値を代入 --#
%hash = (
      'muscat' => { price=>500, santi=>'おかやま', omosa=>150 }
    , 'apple'  => { price=>200, santi=>'あおもり', omosa=>250 }
    , 'banana' => { price=>130, santi=>'たいわん', omosa=>130 }
);

これをTie::IxHashで解決するならば次のようにする。

use Tie::IxHash;

#-- ハッシュとの関連づけ --#
tie %hash,     'Tie::IxHash';
tie %h_muscat, 'Tie::IxHash';
tie %h_apple,  'Tie::IxHash';
tie %h_banana, 'Tie::IxHash';

#-- ハッシュに値を代入 --#
%h_muscat = (price=>500, santi=>'おかやま', omosa=>150);
%h_apple  = (price=>200, santi=>'あおもり', omosa=>250);
%h_banana = (price=>130, santi=>'たいわん', omosa=>130);

%hash = (
      'muscat' => \%h_muscat 
    , 'apple'  => \%h_apple
    , 'banana' => \%h_banana
);

#-- 表示 --#
foreach my $key1 ( keys %hash ){
    print "$key1\n";
    foreach my $key2 ( keys %{$hash{$key1}} ){
        print "    $key2=", $hash{$key1}->{$key2}, "\n"
    }
}

次のように配列としてデータを持ち、取り出すときにハッシュとすることでも実現できる。

use Tie::IxHash;

#------------------------------------------#
#                準  備                    #
#------------------------------------------#
#-- ハッシュとの関連づけ --#
tie %hash, 'Tie::IxHash';

#-- ハッシュに値を代入 --#
%hash = (
      'muscat' => [price=>500, santi=>'おかやま', omosa=>150]
    , 'apple'  => [price=>200, santi=>'あおもり', omosa=>250]
    , 'banana' => [price=>130, santi=>'たいわん', omosa=>130]
);

#------------------------------------------#
#                表  示                    #
#------------------------------------------#
foreach my $key ( keys %hash ){
   my %tmp = ();

    #-- 関連づけ --#
    tie %tmp, 'Tie::IxHash';

    #-- 値の代入 --#
    %tmp = @{$hash{$key}};    #ハッシュもリストのうち

    #-- 表示 --#
    print "$key\n";
    foreach ( keys %tmp ){
        print "    $_ : $tmp{$_}\n";
    }

    untie %tmp;
}

よく(入門者の方に)誤解されるが、'=>'演算子は','演算子と優先順位が異なるだけで、意味はまったく同じである。単純にハッシュに代入する際、キーと値のペアをわかりやすくするなどの目的で設けているだけで、すべて','を使用してハッシュを初期化しても全く問題ない。

以上をふまえておけば、配列とハッシュの初期化方法はまったく同じだということがお分かりいただけるだろう。