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

Hi, I have tried following an example for a Hash of Hash to add an extra layer but it has not worked. When I try to get hold of the third and final hash the keys are not found in the second hash. The data structure is. Database (Pages (Page Hits or instances))) I can get hold of the pages but not the page instances. Here is the code:
dbmopen (%hitsdb,$db,0755) || print "can't dbmopen hitsb: $!"; if ($hitsdb{$page}) { $key = "$Year$Month$hour$referer"; print $key; if ($hitsdb{$page}->{$key}) { $hitsdb{$page}->{$key}->{'counter'}++; print "increment instance"; } else { %hitsdb = ( $page => { $key => { 'year' => $Year, 'month' => $Month, 'day' => $month_day, 'referer' => $referer, 'counter' => 1 } } ); print "new instance"; } } else { $key = "$Year$Month$hour$referer"; print $key; %hitsdb = ( $page => { $key => { 'year' => $Year, 'month' => $Month, 'day' => $month_day, 'referer' => $referer, 'counter' => 1 } } ); print "new instance and page"; }

Replies are listed 'Best First'.
Re: Hash of Hashes of Hashes
by RMGir (Prior) on Aug 16, 2002 at 11:55 UTC
    The problem is that hash references (what {} returns) are local to the process that created them.

    So when you save those references in your dbm, they're only going to be valid for the current process, and won't work when your program runs again.

    The other problem is that every time you create a new key, you're overwriting your whole hash.

    # your else clause, rewritten %{$hitsdb->{$page}->{$key}}={ 'year' => $Year, 'month' => $Month, 'day' => $month_day, 'referer' => $referer, 'counter' => 1 }; print "new instance";
    Otherwise, you wipe out all the old data every time you add a new key. You've got the same problem in your other else branch, which you don't really need, since adding a new instance will auto-vivify the page if it's not already there.
Re: Hash of Hashes of Hashes
by Abigail-II (Bishop) on Aug 16, 2002 at 12:11 UTC
    Note that if a $key isn't found for a specific $page, you remove everything that's in the hash and start with a fresh hash, with a single $page and a single $key.

    Abigail

Re: Hash of Hashes of Hashes
by ehdonhon (Curate) on Aug 16, 2002 at 12:48 UTC

    In cases where you aren't sure what is going on with a nested structure, always remember that Data::Dumper is your friend.

    use Data::Dumper; print Dumper ( \%hitsdb );
      actually, YAML is much more readable.
Re: Hash of Hashes of Hashes
by RMGir (Prior) on Aug 16, 2002 at 15:15 UTC
    You've got a problem with your assignment here:
    ##################### # LINE 85 BELOW # ##################### $hitsdb{$page}{$key => { 'year' => $Year, 'month' => $Month, 'day' => $month_day, 'referer' => $referer, 'counter' => 1 }};
    You're not setting $hitsdb{$page}{$key}, you're using $hitsdb{$page}{$key => ...} and doing nothing with it.

    You probably meant to use:

    $hitsdb{$page}{$key} = { 'year' => $Year, 'month' => $Month, 'day' => $month_day, 'referer' => $referer, 'counter' => 1 };
Re: Hash of Hashes of Hashes
by Anonymous Monk on Aug 16, 2002 at 13:22 UTC
    Thanks, I have modified the code but it is still not working.... I have been looking at various examples but can not find anything that is helping me with this problem.

    Why is would my code be rewriting the hash from scrap? Or rather what errors would I have made in order for it to rewrite the hash?

    I believe I have removed any modifications to references but I am still not modifying the 'counter' property. Maybe I miss understood what you were explaining to me.

    Also I have used the {key1}{key2} format rather than the -> format, this allows the program to execute but what is the difference?

    Thanks in advance for all help.

    Here is the modified code:
    #Open database dbmopen (%hitsdb,$db,0755) || print "can't dbmopen hitsb: $!"; $key = "$Year$Month$hour$referer"; if ($hitsdb{$page} || $hitsdb{$page}{$key}) { $hitsdb{$page}{$key}{'counter'}++; print "appended\n"; } else { $hitsdb{$page}{$key} = { 'year' => $Year, 'month' => $Month, 'day' => $month_day, 'referer' => $referer, 'counter' => 1 }; print "new instance and page\n"; } #Close database dbmclose (%hitsdb) || print "can't dbmclose hitsdb: $!";

Re: Hash of Hashes of Hashes
by James (Initiate) on Aug 16, 2002 at 14:52 UTC
    Sorry to do this again, I have been spending sometime playing with the code and trying to get it to work. I now have a warning which I have searched for both here and on google but can not find anything about it.

    Useless use of hash element in void context at D:\Apache\cgi-bin\hitscounter.pl line 85.

    This is the code here:
    #Open database dbmopen (%hitsdb,$db,0755) || print "can't dbmopen hitsb: $!"; $key = "$Year$Month$hour$referer"; if ($hitsdb{$page} && $hitsdb{$page}{$key}) { $hitsdb{$page}{$key}{'counter'}++; print "appended\n"; } else { ##################### # LINE 85 BELOW # ##################### $hitsdb{$page}{$key => { 'year' => $Year, 'month' => $Month, 'day' => $month_day, 'referer' => $referer, 'counter' => 1 }}; print "new instance\n"; } #Close database dbmclose (%hitsdb) || print "can't dbmclose hitsdb: $!";
Re: Hash of Hashes of Hashes
by James (Initiate) on Aug 16, 2002 at 15:52 UTC
    Okay I have fixed this, I had tried this method before but it does not appear to add the new hash to the base hash.

    As aside what was I doing with the syntax I had?

    I am mystified as to what is going on in my code, or rather why the counter field is always displayed as 0, and only the 'new instance' debug message is ever given even though Year, Month, Referer and Hour are identical on subsequential calls.