Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine

Tie a hash of hashes?

by Anonymous Monk
on Feb 02, 2006 at 20:14 UTC ( #527418=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I was wondering if anyone knows if its possible to use Tie::IxHash (or something similar) in such a way that all hashes retain their key order.

My situation is this:

I've got a text file that needs modifying, and it is structured remarkably similar to a hash of hashes (I just need to search and replace '()' with '{}' and '=' with '=>'). Because of the structure, creating a hash with a 'do' statement is very easy. The problem is, after I've performed the necessary changes, I'd like to output the file again in the same order. Usually when I need to preserve the key order for a hash, I use 'Tie::IxHash', but since I'm creating the hash of hashes from a file, I can't tie second level of hashes with 'Tie::IxHash'.

use Data::Dumper; use Tie::IxHash; tie %data, 'Tie::IxHash'; do "File"; $data{SomeMoreStuff}{SomeMoreData} = 'x'; print Dumper \%data;
%data = ( SomeStuff => { SomeData => 'a', SomeMoreData =>'b', OtherData =>'c', }, SomeMoreStuff => { SomeData =>'a', SomeMoreData => 'b', SomeExtraData => 'c', OtherData => 'd' } );
The output looks like:
$VAR1 = { 'SomeStuff' => { 'OtherData' => 'c', 'SomeMoreData' => 'b', 'SomeData' => 'a' }, 'SomeMoreStuff' => { 'OtherData' => 'd', 'SomeMoreData' => 'x', 'SomeExtraData' => 'c', 'SomeData' => 'a' } };
The output I want:
$VAR1 = { 'SomeStuff' => { 'SomeData' => 'a' 'SomeMoreData' => 'b', 'OtherData' => 'c', }, 'SomeMoreStuff' => { 'SomeData' => 'a' 'SomeMoreData' => 'x', 'SomeExtraData' => 'c', 'OtherData' => 'd', } };

Anyone have any thoughts on how to tie the second level hashes without needing to parse the file line by line and tie each hash as its created?


Comment on Tie a hash of hashes?
Select or Download Code
Re: Tie a hash of hashes?
by kutsu (Priest) on Feb 02, 2006 at 20:24 UTC

    Looking at your data it seems you might just want sort { $b cmp $a }, ie. reverse alphabetically. Or was this just the way you happened to type it and the order isn't guaranteed?

    Update: Somehow switched SomeData and SomeMoreData in the example

Re: Tie a hash of hashes?
by ikegami (Pope) on Feb 02, 2006 at 20:24 UTC

    The problem:

    You need to convert the hashes into Tie::IxHashes.

    Solution 1:

    You could traverse %data and convert the appropriate hashes.

    Solution 2:

    You could create a subclass of Tie::IxHash to override its method. When a hash reference is added to the hash, tie the referenced hash if it isn't already tied. Don't forget to untie when appropriate.

    Update: Time for a break! Neither solution works, because the order is already lost by the time the hashes can be converted.

    What you need to do is to override the behaviour of the hash constructor { ... } at compile time, and I don't see any simple ways of doing that. The complex ways are 1) use a source filter and 2) replace the Perl compiler by parsing the file yourself.

Re: Tie a hash of hashes?
by ikegami (Pope) on Feb 02, 2006 at 20:47 UTC
    I just need to search and replace '()' with '{}' and '=' with '=>'.

    Instead of replacing
    ( ... )
    { ... }
    replace it with
    new_ordered_hash( ... )
    and define new_ordered_hash as follows:

    sub new_ordered_hash { my %ordered_hash; tie %ordered_hash, 'Tie::IxHash'; %ordered_hash = @_; return \%ordered_hash; }


    Don't forget to untie the lower level hashes when appropriate.

    Note: This falls under "2) parsing the file yourself", from my previous post.

      Ah, good thought. Initial testing looks promising.

      Thanks for the input!
Re: Tie a hash of hashes?
by japhy (Canon) on Feb 03, 2006 at 03:43 UTC
      Is that module going to work in this situation?

      The module's description on CPAN says it doesn't work when creating a hash reference, which is what I'm doing, isn't it?

      When I try it with this code:
      use Data::Dumper; use Tie::Autotie 'Tie::IxHash'; tie %data, 'Tie::IxHash'; do "File"; print Dumper \%data;
      I get the following output:
      $VAR1 = { 'SomeStuff' => {}, 'SomeMoreStuff' => {} };
      Which seems to support the module's description that its not going to work in this situation.
        Tie::IxHash doesn't even work in that situation. The problem is that when you use a hash reference altogether -- that is, $hash{x} = { ... } -- you are creating a hash reference in Perl, and that is not "caught" by Tie::IxHash. You can't achieve the desired goal the way File is set up. You'll need to change it, no matter what you do.

        Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
        How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://527418]
Approved by kutsu
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others perusing the Monastery: (5)
As of 2014-11-28 06:22 GMT
Find Nodes?
    Voting Booth?

    My preferred Perl binaries come from:

    Results (193 votes), past polls