tags:

views:

71

answers:

4

In perl, I'm trying to build up a hash of a list of lists. It looks something like this:

my %entries;
while(<>)
{
    if(/complicated regex ommitted/)
    {
        my @entry = ($2, $3, $4);
        if(exists $entries{$1})
        {
            push @{$entries{$1}}, @entry;
        }
        else
        {
            $entries{$1} = @entry;
        }
}

The resulting hash has all of the keys I expect, but the value "list of lists" isn't being built up correctly. What am I doing wrong?

Edit: Maybe there is something wrong with how I'm trying to access the resulting hash.. Here is that code

foreach $key (keys %entries)
{
    my $size = {@entries{$key}};
    # just says "HASH(0xaddress)"?
    print "$key: $size\n"; 
    foreach(@{entries{$key}})
    {
        # loop just goes through once, prints out just " : "
        print "\t$_[0]: $_[1] $_[2]\n";
    }
}   
+4  A: 

You're pushing a list, rather than a listref. Try:

push @{$entries{$1}}, \@entry;

(You don't need to check for the existence of the field first.. if it didn't already exist, it will be created when you push to it, through the wonders of autovivication.)

Ether
Uh, should that not be `(\@entry)` in the `else` case?
Svante
I tried changing to listrefs (including making the else an explict list) but still getting the same result. I suspect maybe my code to iterate over the elements is at fault then?
tjm2001
@Svante: I've updated my answer to take advantage of autovivication.
Ether
A: 

You need to push a reference onto the list, otherwise the lists just get appended so that you get a simple list (see the manual on push). "List of list" is always a "list of references to lists" in Perl.

Svante
+3  A: 

Perl has a feature called autovivification that makes scaffolding spring to life when you need it. This keeps your code simple:

my %entries;
while(<>)
{
    if (/complicated regex ommitted/)
    {
        my($key,@entry) = ($1, $2, $3, $4);
        push @{ $entries{$key} }, \@entry;
    }
}

There's no need to check whether this is the first group of entries for a given key.

To dump the contents of %entries, use code that resembles

foreach my $key (sort keys %entries)
{
    my $n = @{ $entries{$key} };
    print "$key ($n):\n";

    foreach my $l (@{ $entries{$key} })
    {
        print "\t$l->[0]: $l->[1] $l->[2]\n";
    }
}
Greg Bacon
Thanks! One question on the output though, instead of printing out the elements of individual entries, it's printing out ARRAY(0xaddress). Any idea why?
tjm2001
@tjm2001 See my updated answer. I missed the bit about the values being lists of lists and expected them to be flat lists.
Greg Bacon
A: 

while ( <> ) {

if ( / (r) (e) (g) (e) x /x ) {
    push @{ $entry{ $1 } }, [ $2, $3, $4 ];
}

}

or in 1 line:

/(r)(e)(g)(e)x/ and push @{$entry{$1}}, [$2, $3, $4] while <>;

and to show them:

use Data::Dumper; ... print Dumper \%entry;

Dr.Ruud