Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

variable number of hash of hashes

by Boetsie (Sexton)
on Jul 23, 2012 at 23:54 UTC ( [id://983261]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Monks,

I've been searching for a solution to write and read a variable number of hashes into hashes. So far I had no luck. Hope you guys can help me out!

So my data is something like this;

string1,string2,string3,string4 string3,string4 string1,string2,string3

I want each line into a hash of hashes of hashes... like this;

%hash{'string1'}{'string2'}{'string3'}{'string4'}++; %hash{'string3'}{'string4'}++; %hash{'string1'}{'string2'}{'string3'}++;

How can i fill this hash properly? Note, I do not know how many strings I can have in the array, so there even might be a string 'string1...string10'.

After I have filled it, how can I read it out? Normally i do it something like this;

foreach my $k1 (keys %hash){ foreach my $k2 (keys %hash{$k1}){ ... } }

But then I know the number of hashes on beforehand.

Any advice would be great.

Thanks, Boetsie

Replies are listed 'Best First'.
Re: variable number of hash of hashes
by GrandFather (Saint) on Jul 24, 2012 at 01:21 UTC

    Using iteration for populating the hash is fairly simple, but recursion is tidy for dumping the structure:

    #!/usr/bin/perl use strict; use warnings; my %hash; while (<DATA>) { my $parent = \%hash; chomp; for my $str (split ',') { $parent = $parent->{$str} ||= {}; $parent->{count}++ } } dumpHash (\%hash, ''); sub dumpHash { my ($hash, $indent) = @_; for my $child (sort keys %$hash) { next if $child eq 'count'; print "$indent$child $hash->{$child}{count}\n"; dumpHash ($hash->{$child}, "$indent "); } } __DATA__ string1,string2,string3,string4 string3,string4 string1,string2,string3 string1,string3,string5

    Prints:

    string1 3 string2 2 string3 2 string4 1 string3 1 string5 1 string3 1 string4 1

    Note that you need to keep a count and a hash (ref) of any child nodes for each parent node. Incrementing a hashref will not lead to a happy life!

    True laziness is hard work

      Thank you all, great solutions!

      I've implemented 'GrandFather's' method, since this was working straight away and seemed the be the most easy method.

      Many thanks! Boetsie

Re: variable number of hash of hashes
by roboticus (Chancellor) on Jul 24, 2012 at 00:06 UTC

    Boetsie:

    For a trivial solution, you can use recursion: Your degenerate/simple cases is when you have a single string and a hash reference--you can simply:

    ++$$hr{$string};

    If you have more than one string, though, you can instead get the hash referenced by the first string, and then call the function recursively with the new hash reference and the array of strings less the first one.

    So you could end up with (untested):

    my %hash; my @tests = ( [ 'string1', 'string2', 'string3', 'string4' ], [ 'string3', 'string4' ], [ 'string1', 'string2', 'string3' ], ); for my $t (@tests) { my $hr = \%hash; my @strings = @$t; increment($hr, @strings); } sub increment { my ($hr, @strings) = @_; if (@strings == 1) { # simple case ++$$hr{$strings[0]}; } else { # Peel the first string from the list my $first_string = shift @strings; # Get the associated hash reference $hr = $$hr{$first_string}; # Call ourself... increment($hr, @strings); } }

    Of course, there are a few hairy areas in there. What do you do if your first list of strings is 'bar', 'foo' and then your next list is 'bar', 'foo', 'baz'? After the first list, the second level of the hash contains your incremented value instead of a hash reference. How will you handle that? That's just one of the subtleties you'll have to deal with. (On reviewing the test cases you provided, you actually get that sort of situation! Hint: perldoc -f ref.)

    Have fun!

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: variable number of hash of hashes (Diver)
by tye (Sage) on Jul 24, 2012 at 00:24 UTC
Re: variable number of hash of hashes
by i5513 (Pilgrim) on Jul 24, 2012 at 08:21 UTC

    Hello,

    Be careful with your code:

    1. %hash{'string1'}{'string2'}{'string3'}{'string4'}++; 2. %hash{'string3'}{'string4'}++; 3. %hash{'string1'}{'string2'}{'string3'}++;

    In 1 you are defining {'string1'}{'string2'}{'string3'} with a hash, and in 3 you want to add 1 to that hash ... which is not that you want

    Use a final string like "n" or "count", like GrandFather propose:

    1. $hash{'string1'}{'string2'}{'string3'}{'string4'}{n}++; 2. $hash{'string3'}{'string4'}{n}++; 3. $hash{'string1'}{'string2'}{'string3'}{n}++;
    Regards,
Re: variable number of hash of hashes
by grizzley (Chaplain) on Jul 24, 2012 at 06:43 UTC

    You could treat it as string, transform to final version and do eval:

    #!perl -l @strings = <DATA>; for(@strings) { chomp; s/,/'}{'/g; s/^/\$hash{'/; s/$/'}++/; print "eval $_"; eval $_; } use Data::Dumper; print Dumper \%hash; __DATA__ string1,string2,string3,string4 string3,string4 string1,string2,string3
    Output:
    c:\>test.pl eval $hash{'string1'}{'string2'}{'string3'}{'string4'}++ eval $hash{'string3'}{'string4'}++ eval $hash{'string1'}{'string2'}{'string3'}++ $VAR1 = { 'string3' => { 'string4' => 1 }, 'string1' => { 'string2' => { 'string3' => 5393161 } } };
    But as you can see there is a problem to solve: 'string3' => 5393161 which was created as reference to hash, but then replaced by value. What do you want to do in such situation?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://983261]
Approved by GrandFather
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (6)
As of 2024-04-23 14:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found