Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Stuck with Creating Multi-Level hash

by sara2005 (Scribe)
on May 26, 2006 at 22:33 UTC ( [id://551944] : perlquestion . print w/replies, xml ) Need Help??

sara2005 has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

I am stuck with creating a multi-level hash and look forward for some help from the community.

I have to create a cgi that reads a data file, displays the hierarchy in a windows explorer format and then allows the user to execute some program based on the selection.

I found that I can use the CGI-Explorer module in CPAN to create a windows explorer type interface. To achieve that, I need to create a multi-level hash data structure from the data file.

Now, my data file looks as follows:-

[Add Menu] Name = L1A Title = L1A [Done] [Add Entry] Action = None Text = L2A [Done] [Add Entry] Action = None Text = Second [Done] ----------------------- [Add Menu] Name = L2A Title = L1A - L2A [Done] [Add Entry] Action = None Text = L3A [Done] [Add Entry] Action = None Text = L3B [Done] ------------------------- [Add Menu] Name = Second Title = L1A - Second [Done] [Add Entry] Action = exe command Text = Third1 [Done] [Add Entry] Action = exe command Text = L3B [Done] ------------------------- [Add Menu] Name = L3A Title = L1A - L2A - L3A [Done] [Add Entry] Action = exe command Text = L4A [Done] [Add Entry] Action = exe command Text = L3B1 [Done] ------------------------- [Add Menu] Name = L3B Title = L1A - L2A - L3B [Done] [Add Entry] Action = exe command Text = L4A [Done] ------------------------- etc.. etc..

and I need to create a mutli-hash structure as shown below from the data file:-

$VAR1 = { 'L1A' => { 'L2A' => { 'L3A' => { 'L4A' => 'exe command', 'L3B1' => 'exe command' }, 'L3B' => { 'L4A' => 'exe command' } }, 'Second' => { 'L3B' => 'exe command', 'Third1' => 'exe command' } } };

So, I decided to read the data file, create an 'array of hashes' that would combine both the 'Add Menu' and the corresponding 'Add Entry', which would look as follows:-

$VAR1 = [ { 'Counter' => 1, 'Action' => 'None', 'Name' => 'L1A', 'Text' => 'L2A', 'Title' => 'L1A' }, { 'Counter' => 2, 'Action' => 'None', 'Name' => 'L1A', 'Text' => 'Second', 'Title' => 'L1A' }, { 'Counter' => 3, 'Action' => 'None', 'Name' => 'L2A', 'Text' => 'L3A', 'Title' => 'L1A - L2A' }, { 'Counter' => 4, 'Action' => 'None', 'Name' => 'L2A', 'Text' => 'L3B', 'Title' => 'L1A - L2A' }, { 'Counter' => 5, 'Action' => 'exe command', 'Name' => 'Second', 'Text' => 'Third1', 'Title' => 'L1A - Second' }, { 'Counter' => 6, 'Action' => 'exe command', 'Name' => 'Second', 'Text' => 'L3B', 'Title' => 'L1A - Second' }, { 'Counter' => 7, 'Action' => 'exe command', 'Name' => 'L3A', 'Text' => 'L4A', 'Title' => 'L1A - L2A - L3A' }, { 'Counter' => 8, 'Action' => 'exe command', 'Name' => 'L3A', 'Text' => 'L3B1', 'Title' => 'L1A - L2A - L3A' }, { 'Counter' => 9, 'Action' => 'exe command', 'Name' => 'L3B', 'Text' => 'L4A', 'Title' => 'L1A - L2A - L3B' } ];

Then, I wanted to go over each array element, get the 'Title' and split that using the '-'(hypen) separator, and build the multi-hash. For example, the first entry in the mutli-hash would be

$Menu_Multi_Hash{'L1A'}{'L2A'} = 'none'; etc.. etc.. and for the last one, it will be $Menu_Multi_Hash{'L1A'}{'L2A'}{'L3B'}{'L4A'} = 'exe command'; (please note that the first three elements are the ones arrived by spl +itting the 'Title', the fourth is the 'Text' and the right hand side +is the 'Action')

The problem is that I am not able write a generic subroutine that can take the 'Title', 'Text' and 'Action' and build the multi-hash, because the 'Title' can have anywhere from one element to ten.

I am not sure if this is a good approach. Any advice would be of great help.. Thanks..

Below is the code is for creating the array of hashes from the data file.

use Data::Dumper; use strict; open (FILE, "filename"); my $menu_counter = 0; my $MenuName; my $MenuTitle; my %temp_hash; my $line_read; my @MenuList; #Array of Hashes while (<FILE>) { next if /^\s*$/; if ( /^\[Add Menu]/../^\[Done]/) { $line_read = $_; if ( $line_read=~/^\s*(\w+)\s=\s(.*)/ ) { if ($1 eq "Name") { $MenuName = $2; } elsif ($1 eq "Title") { $MenuTitle = $2; } } } elsif ( /^\[Add Entry]/../^\[Done]/) { $line_read = $_; if ( $line_read=~/^\s*(\w+)\s=\s(.*)/ ) { if ( $1 eq "Action") { if ($menu_counter != 0) { push @MenuList, {%temp_hash}; } $menu_counter++; %temp_hash = &initialize_structure( $1, $2, $menu_coun +ter, $MenuName, $MenuTitle ); } else { &createpair( $1, $2); } } } } push @MenuList, {%temp_hash}; print Dumper(\@MenuList); sub initialize_structure() { my ( $key, $value, $counter, $menuname, $menutitle) = @_; my %temp_var; %temp_var = ( Name => "", Title => "", Action => "", Text => "", Counter => "", ); $temp_var{$key} = $value; $temp_var{'Counter'} = $counter; $temp_var{'Name'} = $menuname; $temp_var{'Title'} = $menutitle; return %temp_var; }; # subroutine to populate rest of the data structure sub createpair() { my($key, $value) = @_; $temp_hash{$key} = $value; };

Replies are listed 'Best First'.
Re: Stuck with Creating Multi-Level hash
by GrandFather (Saint) on May 27, 2006 at 02:17 UTC
Re: Stuck with Creating Multi-Level hash
by merlyn (Sage) on May 27, 2006 at 00:09 UTC
    Skimming over your post quickly, I noticed this:
    $Menu_Multi_Hash{'L1A'}{'L2A'} = 'none'; .... $Menu_Multi_Hash{'L1A'}{'L2A'}{'L3B'}{'L4A'} = 'exe command';
    That's not possible. $Menu_Multi_Hash{'L1A'}{'L2A'} can either be data, or the hashref pointing to the next level, but never both.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      Below is the simple code that builds the multi-hash data structure..

      use Data::Dumper; use Tie::Autotie 'Tie::IxHash'; tie my %Menu_Multi_Hash, 'Tie::IxHash'; $Menu_Multi_Hash{'L1A'}{'L2A'} = undef; $Menu_Multi_Hash{'L1A'}{'Second'} = undef; $Menu_Multi_Hash{'L1A'}{'L2A'}{'L3A'} = undef; $Menu_Multi_Hash{'L1A'}{'L2A'}{'L3B'} = undef; $Menu_Multi_Hash{'L1A'}{'L2A'}{'L3A'}{'L4A'} = '/path/to/exe'; $Menu_Multi_Hash{'L1A'}{'L2A'}{'L3A'}{'L3B1'} = '/path/to/exe'; $Menu_Multi_Hash{'L1A'}{'L2A'}{'L3B'}{'L4A'} = '/path/to/exe'; $Menu_Multi_Hash{'L1A'}{'Second'}{'L3B'} = '/path/to/exe'; $Menu_Multi_Hash{'L1A'}{'Second'}{'Third1'} = '/path/to/exe'; print Dumper(\%Menu_Multi_Hash);

      The output is as follows:

      $VAR1 = { 'L1A' => { 'L2A' => { 'L3A' => { 'L4A' => '/path/to/exe', 'L3B1' => '/path/to/exe' }, 'L3B' => { 'L4A' => '/path/to/exe' } }, 'Second' => { 'L3B' => '/path/to/exe', 'Third1' => '/path/to/exe' } } };

      Hence, I was under the impression that if I can achieve this in an efficient way, I will get a multi-hash to use with the CGI:Explorer module.

      Please advice if my approach is wrong.

      What? No column to refer to which solved this problem already a long time ago? Are you feeling OK Merlyn? ;-)

      CountZero

      "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law