Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Hash Ref: Need to find out how many children I have :-)

by rupesh (Hermit)
on Dec 16, 2005 at 09:41 UTC ( #517186=perlquestion: print w/replies, xml ) Need Help??
rupesh has asked for the wisdom of the Perl Monks concerning the following question:


Suppose a hash ref exists like this:
$hash->{Grand}->{Parent1}->{Child1}->{name}; $hash->{Grand}->{Parent1}->{Child1}->{age}; $hash->{Grand}->{Parent1}->{Child2}->{name}; $hash->{Grand}->{Parent1}->{Child2}->{age}; $hash->{Grand}->{Parent1}->{Child3}->{name}; $hash->{Grand}->{Parent1}->{Child3}->{age}; $hash->{Grand}->{Parent2}->{Child1}->{name}; $hash->{Grand}->{Parent2}->{Child1}->{age}; $hash->{Grand}->{Parent2}->{Child2}->{name}; ...
I need to loop through "Grand" and get the number of Parents; and for each Parent, get number of Children; and for each children, print their names and ages.
How do I do this?
The actual rows have further more levels. The key importance here is perfomance.


Thanks,
Rupesh.

Replies are listed 'Best First'.
Re: Hash Ref: Need to find out how many children I have :-)
by davido (Archbishop) on Dec 16, 2005 at 09:57 UTC

    Data::Walk will walk the structure easily for you, and by hand crafting a wanted() sub, you can set up counters for various depths.

    Or if you already have worked out how to walk the structure, remember that my $count = keys %{$hash};, or later my $count = keys %{$hash->{Group}} will return a count of the number of keys in a given level.


    Dave

Re: Hash Ref: Need to find out how many children I have :-)
by tirwhan (Abbot) on Dec 16, 2005 at 10:02 UTC

    Update: Oops, misread the question. Clearly not the answer.

    Update2: This should do it though :-)

    Something like this?

    use warnings; use strict; my %count; my $hash = {Grand => { Parent1 => {Child1 => { name => "me", age => 99 }, Child2 => { name => "you", age => 42 }, Child3 => { name => "she", age => 1 }}}}; recurse_or_count($hash->{Grand},"Grand"); sub recurse_or_count { my ($cur,$string)=@_; for my $elem (keys %$cur) { if (ref($cur->{$elem}) eq "HASH") { $count{$string}++; recurse_or_count ($cur->{$elem},$string."->$elem"); } else { $count{$string}->{$elem}=$cur->{$elem}; } } } for my $key (sort keys %count) { if (ref($count{$key}) eq "HASH") { for my $data (sort keys %{$count{$key}}) { print "$key has $data $count{$key}->{$data}\n"; } }else { my $child = $count{$key} > 1 ? "children" : "child"; print "$key has $count{$key} $child\n"; } } __OUTPUT__ Grand has 1 child Grand->Parent1 has 3 children Grand->Parent1->Child1 has age 99 Grand->Parent1->Child1 has name me Grand->Parent1->Child2 has age 42 Grand->Parent1->Child2 has name you Grand->Parent1->Child3 has age 1 Grand->Parent1->Child3 has name she

    Update3: and if you don't mind the child data being printed out before the child count, here's a more efficient version of that:

    use warnings; use strict; my %count; my $hash = {Grand => { Parent1 => {Child1 => { name => "me", age => 99 }, Child2 => { name => "you", age => 42 }, Child3 => { name => "she", age => 1 }}}}; recurse_or_count($hash->{Grand},"Grand"); sub recurse_or_count { my ($cur,$string)=@_; for my $elem (sort keys %$cur) { if (ref($cur->{$elem}) eq "HASH") { $count{$string}++; recurse_or_count ($cur->{$elem},$string."->$elem"); } else { print "$string has $elem $cur->{$elem}\n"; } } } for my $key (sort keys %count) { my $child = $count{$key} > 1 ? "children" : "child"; print "$key has $count{$key} $child\n"; } __OUTPUT__ Grand->Parent1->Child1 has age 99 Grand->Parent1->Child1 has name me Grand->Parent1->Child2 has age 42 Grand->Parent1->Child2 has name you Grand->Parent1->Child3 has age 1 Grand->Parent1->Child3 has name she Grand has 1 child Grand->Parent1 has 3 children

    Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. -- Brian W. Kernighan
Re: Hash Ref: Need to find out how many children I have :-)
by davidrw (Prior) on Dec 16, 2005 at 13:43 UTC
    The actual rows have further more levels. The key importance here is perfomance.
    Can you put this all into a database? Once there, gets a lot easier (and faster if indexed/structured properly).
    users user_id first_name last_name user_age -- !!!! really should be stored as birthdate or birthyear parent_user_rlt parent_id child_id SELECT grand.user_id as grandparent_id, parent.user_id as parent_id, child.user_id as child_id, child.first_name as child_first, child_last_name as child_last FROM users as grand LEFT JOIN parent_user_rlt as g2p ON g2p.parent_id = grand.user_id LEFT JOIN users as parent ON parent.user_id = g2p.child_id LEFT JOIN parent_user_rlt as p2c ON p2c.parent_id = parent.user_id LEFT JOIN users as child ON child.user_id = g2p.child_id ;
    If you use that query to construct a temp table called "grandchildren", then your answers become things like:
    SELECT distinct parent_id FROM grandchildren; SELECT grandparent_id, count(distinct parent_id) FROM grandchildren +GROUP BY grandparent_id; SELECT grandparent_id, parent_id, count(*) as num_kids FROM grandchi +ldren GROUP BY grandparent_id, parent_id; SELECT child_id, child_first, child_last FROM grandchildren;

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://517186]
Approved by Corion
help
Chatterbox?
[marto]: the ticketing system does not accept calls via email, nor has it a working API. It's tied into Active Directory for authentication and the Solaris boxes aren't on that domain
[Corion]: The one thing I haven't figured out a solution to is how to get an edge-trigger instead of sending an email every 5 minutes if the usage is above 90%. I want one mail when it goes over 90% but no more emails as long as it stays between 90% and 95%.
[Corion]: marto: Clever! ;)
[Corion]: You can only reach me by pager
[Corion]: Maybe the solution would be to launch a cron job every minute that takes two measurements a minute apart and sends a mail if the usage is below on the first and above threshold on the last measurement
[marto]: that's essentially it :)
[marto]: I think the long term solution would be to have sysadmins that do their job, so I don't have to do everything :P
[marto]: they already have an entire BMC patrol system, which they disabled, because it was sending out spurious messages. So rather than fix the issue, or even find out what it was, they turned it off. No messages, can't be any problems, right?
[Corion]: marto: But having open tickets / incidents increases the pressure on them ;) Of course, likely your contract / SLA specifies an upper limit for the number of incidents :-D
[Corion]: marto: Ow ...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (7)
As of 2017-01-24 10:12 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Do you watch meteor showers?




    Results (203 votes). Check out past polls.