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

I've having difficulty with a hash of arrays. I'm creating a dropdown menu system with headers a b .. z under each letter is are menuItems beginning with a b .. z. my structure looks like this;
my @menu_bar_names = qw ( aMenu bMenu ... ); my @MenuItems=""; my %aMenu = { label => "  A", MenuItems => [ @MenuItems ] }; my %bMenu = { label => "  B", MenuItems => [ @MenuItems ] }; my %cMenu = { label => "  C", MenuItems => [ @MenuItems ] };
I used the following to populate each MenuItem array.
foreach my $app (@sorted_applications) { $app = lc $app; if ( "$app" lt "b" ) { push ( @{ $aMenu{MenuItems} }, $app); next; } elsif ($app lt "c" ) { push (@{ $bMenu{MenuItems} },$app); next; ...
And the following to print the first element.
print " @{$aMenu{MenuItems}}[0]\n";
I would like to avoid using $aMenu in the printing and use something like, $abc_menu instead of hard coding $aMenu, $bMenu such as;
foreach my $abc_menu (@menu_bar_names) { print " @{$abc_menu{MenuItems}}[0]\n"; }
but I get the following error. 'Global symbol "abc_menu"; requires explicit package name at...' The error makes sense and I understand why I get it. But I haven't figured out how to get around it. I'm sure it's something simple but I've read so much I've confused myself. Thanks for taking the time to read this.

Replies are listed 'Best First'.
Re: Hashes of Arrays
by davidrw (Prior) on Jun 05, 2006 at 20:43 UTC
    Be sure to always use strict; and use warnings; ... for example this line is incorrect:
    my %aMenu = { label => "  A", MenuItems => [ @MenuItems ] };
    And should be (warnings would have complained about it):
    my %aMenu = ( label => "  A", MenuItems => [ @MenuItems ] );

    As for your root issue, I think you're just looking for another level of hashing ..
    # if @MenuItems actually has something it it, then MenuItems => [@M +enuItems] is fine # but for simplicity i just made it MenuItems => [] my %menus = ( a => { label => "  A", MenuItems => [] }, b => { label => "  B", MenuItems => [] }, c => { label => "  C", MenuItems => [] }, ); #OR: my %menus = map { lc($_) => {label => "  $_", MenuItems => +[]} } 'A' .. 'C'; # then: foreach my $app (map {lc $_} @sorted_applications) { push @{ $menus{$app}->{MenuItems} }, $app; }
    or, if you don't know the A, B, C menus ahead of time, just make them on the fly as needed:
    my %menus; foreach my $app (map {lc $_} @sorted_applications) { $menus{$app} ||= { label => "  ".uc($app), MenuItems => [] }; push @{ $menus{$app}->{MenuItems} }, $app; }
Re: Hashes of Arrays
by philcrow (Priest) on Jun 05, 2006 at 20:36 UTC
    I would trade in the three menu declarations for one with an extra hash level keyed by app name (or actually by lc $app):
    my %Menu = { app1 => { label => "  A", MenuItems => [ @MenuItems ] }, app2 => { label => "  B", MenuItems => [ @MenuItems ] }, app3 => { label => "  C", MenuItems => [ @MenuItems ] }, };
    Then replace other mentions of aMenu, etc. with Menu{lc_app}. For example:
    foreach my $app (@sorted_applications) { $app = lc $app; push @{ $Menu{$app}{MenuItems} }, $app; }
    Phil
Re: Hashes of Arrays
by GrandFather (Saint) on Jun 05, 2006 at 20:55 UTC

    Use the menu letter as the key and defer creation of hash entries until they are needed cleans things up a little. Consider:

    use strict; use warnings; my %menus; my @sorted_applications = qw(app1 App2 bapp bopp cap cop); for my $app (@sorted_applications) { # Create and populate menus $app = lc $app; my $letter = substr $app, 0, 1; if (! exists $menus{$letter}) { #create the menu entry $menus{$letter}{label} = '$nbsp; ' . uc $letter; $menus{$letter}{items} = []; # Not required, but shows intent } push @{$menus{$letter}{items}}, $app; } # Dump the menu structure for my $topMenu (sort keys %menus) { print "$menus{$topMenu}{label}\n"; print " $_\n" for @{$menus{$topMenu}{items}}; }

    Prints:

    $nbsp; A app1 app2 $nbsp; B bapp bopp $nbsp; C cap cop

    DWIM is Perl's answer to Gödel
      Thanks for the suggestions I will test them today.