Tie a hash of hashes?
Anonymous Monk
created: 2006-02-02 15:14:59
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'.


E.g.
Script:
use Data::Dumper;
use Tie::IxHash;
tie %data, 'Tie::IxHash';

  do "File";

  $data{SomeMoreStuff}{SomeMoreData} = 'x';

  print Dumper \%data;
File:
%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?

Thanks!
Re: Tie a hash of hashes?
created: 2006-02-02 15:24:07

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?

Re: Tie a hash of hashes?
created: 2006-02-02 15:24:33

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.

Re: Tie a hash of hashes?
created: 2006-02-02 15:47:14
(I just need to search and replace '()' with '{}' and '=' with '=>').

Instead of replacing
( ... )
with
{ ... }
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;
}

Untested.

Don't forget to [doc://untie|untie] the lower level hashes.

Re^2: Tie a hash of hashes?
created: 2006-02-02 17:19:47
Ah, good thought. Initial testing looks promising.

Thanks for the input!
Re: Tie a hash of hashes?
created: 2006-02-02 22:43:24
This is pretty much exactly what I created Tie::Autotie for.

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
Re^2: Tie a hash of hashes?
created: 2006-02-03 10:51:38
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.
Re^3: Tie a hash of hashes?
created: 2006-02-03 11:05:46
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

perlmonks.org content © perlmonks.org and Anonymous Monk, ikegami, japhy, kutsu

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03