Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Sort a Hash 3 Levels Deep

by Dru (Hermit)
on Dec 09, 2008 at 18:01 UTC ( #729228=perlquestion: print w/replies, xml ) Need Help??
Dru has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I need help with a complex (well for me anyway) sort routine. I have a hash that has various elements, and 3 of those elements are the region, country, and city. Right now I am sorting on the region, but I also need to sort on the country next, and city last.

So, for the following data:
APAC India Delhi APAC China Suzhou APAC Australia Sydney APAC Japan Tokyo EMEA SouthAfrica Johannesburg EMEA Russia Moscow APAC China Shanghai EMEA Germany Munich EMEA France Paris EMEA Italy Venice NLAM Argentina BuenosAires2 NLAM USA NYC NLAM Brazil San Paulo NLAM USA Austin EMEA Italy Rome NLAM USA Boston
I need it to be sorted as such:
APAC Australia Sydney APAC China Shanghai APAC China Suzhou APAC India Delhi APAC Japan Tokyo EMEA France Paris EMEA Germany Munich EMEA Italy Rome EMEA Italy Venice EMEA Russia Moscow EMEA SouthAfrica Johannesburg NLAM Argentina BuenosAires2 NLAM Brazil San Paulo NLAM USA Austin NLAM USA Boston NLAM USA NYC
Here is where I am extracting my hash and how I sort based on region
sub print_stats{ my $subinput = $_[0]; for my $site1 (keys %siteInfo){ #Site has to be in correct region next unless ($siteInfo{$site1}{region} eq $subinput); print "$siteInfo{$site1}{region},$siteInfo{$site1}{country},$site1, $siteInfo{$site1}{totalDevices}, ,$siteInfo{$site1}{totalVulns}, $siteInfo{$site1}{totalS5s},"; } } }
Thanks,
Dru

Perl, the Leatherman of Programming languages. - qazwart

Replies are listed 'Best First'.
Re: Sort a Hash 3 Levels Deep
by GrandFather (Sage) on Dec 09, 2008 at 20:14 UTC

    SuicideJunkie hints at the solution for you in Re: Sort a Hash 3 Levels Deep, but you may need a little elaboration. The following mocks up your data structure, generates a sorted list of keys, then uses your sample for loop to print the result:

    use strict; use warnings; my %siteInfo; while (<DATA>) { chomp; my @parts = split; my $site = ($siteInfo{"Site$."} = {}); @{$site}{qw(region country city)} = @parts; $site->{totalS5s} = $site->{totalVulns} = ++$site->{totalDevices}; } my @sorted = sort { my $siteA = $siteInfo{$a}; my $siteB = $siteInfo{$b}; $siteA->{region} cmp $siteB->{region} or $siteA->{country} cmp $siteB->{country} or $siteA->{city} cmp $siteB->{city} } keys %siteInfo; for my $site1 (@sorted) { my $site = $siteInfo{$site1}; print "$site->{region},$site->{country},$site1,$site->{totalDevice +s}, ,$site->{totalVulns},$site->{totalS5s}\n"; } __DATA__ ...

    Prints (if you replace ... above with the sample data):

    APAC,Australia,Site3,1, ,1,1 APAC,China,Site7,1, ,1,1 APAC,China,Site2,1, ,1,1 APAC,India,Site1,1, ,1,1 APAC,Japan,Site4,1, ,1,1 EMEA,France,Site9,1, ,1,1 EMEA,Germany,Site8,1, ,1,1 EMEA,Italy,Site15,1, ,1,1 EMEA,Italy,Site10,1, ,1,1 EMEA,Russia,Site6,1, ,1,1 EMEA,SouthAfrica,Site5,1, ,1,1 NLAM,Argentina,Site11,1, ,1,1 NLAM,Brazil,Site13,1, ,1,1 NLAM,USA,Site14,1, ,1,1 NLAM,USA,Site16,1, ,1,1 NLAM,USA,Site12,1, ,1,1

    Perl's payment curve coincides with its learning curve.
      Thank you. That is what I was trying to do.

      Dru

      Perl, the Leatherman of Programming languages. - qazwart
Re: Sort a Hash 3 Levels Deep
by kennethk (Abbot) on Dec 09, 2008 at 18:11 UTC
    An appropriate example for sorting structures can be found in Sort lists/Data Structures. How is your hash organized? That is not entirely clear from you example. Your print suggests it is not, in fact, 3 levels deep but is a set of three-element hashes, which is bad for your problem as presented. Is the key ($site1) just a anonymous hash? Example code for how you build your hash would be very helpful.
Re: Sort a Hash 3 Levels Deep
by SuicideJunkie (Vicar) on Dec 09, 2008 at 18:08 UTC
    Something like this perhaps?
    foreach my $key1 (sort keys( %hash ) ) { foreach my $key2 (sort keys( %{$hash{$key1}} ) ) { foreach my $key3 (sort keys( %{$hash{$key1}{$key2}} ) ) { } } }
    PS: If it is not actually three levels deep, and you simply want to sort based on three different hash values, then you'll want to do something more like this:
    @SortedArrayOfHashes = sort { return $a->{'key1'} cmp $b->{'key1'} or # if these are eq +ual try the comparison below $a->{'key2'} cmp $b->{'key2'} or # if these are eq +ual try below $a->{'key3'} cmp $b->{'key3'} #if these are equal, + then we give up; they're the same. } @ArrayOfHashes;
Re: Sort a Hash 3 Levels Deep
by MidLifeXis (Monsignor) on Dec 09, 2008 at 18:29 UTC

    It looks to me as if you are not doing a sort, but rather grouping only. The order is the order returned by the keys statement, which is undefined (disclaimer: it is snowing here; I had to battle Jack Frost twice, and the snowplow once this morning, so I may be as hazy as the sky outside of my window).

    To sort, you could nested loops on sort keys... (as noted by SuicideJunkie above) at each level of the data structure. This might be the simplest option.

    You could also extract all levels of the structure's keys ([[reg1,ctry1,city1][reg2,ctry2,city2]...]), sort these, and then extract the data from the hash given these sorted values.

    You could also join the keys into a single string for each entry (["reg1:ctry1:city1","reg2:ctry2:city2"...]), sort these, and extract the data from the hash using these sorted values (be careful of what you choose to use as the delimiter here).

    On the last two, a Schwarzian Transform might (or might not) buy you some performance or understanding for your maintenance programmers, but you would need to benchmark the options to be sure what works best in your situation.

    This all depends on your data structures (see kennethk above), how you build them, and how large your data set is.

    Update: Added some clarification and credit. I was way too slow posting my response ;-)

    --MidLifeXis

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://729228]
Approved by toolic
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (7)
As of 2018-06-21 20:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?



    Results (119 votes). Check out past polls.

    Notices?