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

Hello Everyone,
I have a Hash of Hashes given as below:
'Method1' => { 'Name' => 'ABC', 'File' => 'Beta.dat', 'Category' => 'Methods', 'Params' => 'ARGV' }, 'Method2' => { 'Name' => 'MNO', 'File' => 'Alpha.dat', 'Category' => 'Functions', 'Params' => 'ARGV' }, ... ... ... <br>
I'd like to print out the entire hash of hashes by sorting it on Category then on File and then on Name
I do something like this currently
$temp1 = ""; $temp2 = ""; $temp3 = ""; for my $key1 (reverse sort { $myData{$b}{'Category'} cmp $myData{$a}{' +Category'}} keys %myData) { $temp2 = ""; my $categoryCounter=0; my $category = $myData{$key1}{'Category'}; if($temp1 !~ /$category/i){ $temp1 = $category; } for my $key2 (reverse sort {$myData{$b}{'File'} cmp $myData{$a +}{'File'}} keys %myData) { my $fileName = $myData{$key2}{'File'}; if($temp2 !~ /$fileName/i){ $temp2 = $fileName; } $temp3 =""; my $fileCounter = 0; for my $key3 (reverse sort {$myData{$b}{'Name'} cmp $myDat +a{$a}{'Name'}} keys %myData) { my $methodNo = $mydata{$key3}{'Name'}; my $fileNameInner = $mydata{$key3}{'File'}; if ($temp3 !~ /$methodNo/i) { if (($category =~ /$mydata{$key3}{'Category'}/i) & +& ($fileName =~ /$fileNameInner/i)) { if($categoryCounter == 0){ print "$category\n"; $categoryCounter++; } if($fileCounter == 0){ print "$fileName\n"; $fileCounter++; } print "$methodNo\n"; } $temp3 = $fileNameInner; } } $fileCounter = 0; } $categoryCounter = 0; }
While this works, It does take a lot of time, Can someone suggest a way to do it faster?

Replies are listed 'Best First'.
Re: Printing a Tree of Hash of hashes sorted by value
by jethro (Monsignor) on Oct 13, 2008 at 20:17 UTC
    #!/usr/bin/perl use warnings; use strict; my %myData= ( 'Method1' => { 'Name' => 'ABC', 'File' => 'Beta.dat', 'Category' => 'Methods', 'Params' => 'ARGV' }, 'Method2' => { 'Name' => 'MNO', 'File' => 'Alpha.dat', 'Category' => 'Functions', 'Params' => 'ARGV' }, 'Method3' => { 'Name' => 'ABA', 'File' => 'Beta.dat', 'Category' => 'Methods', 'Params' => 'ARGV' }, 'Method4' => { 'Name' => 'MNZ', 'File' => 'Alpha.dat', 'Category' => 'Functions', 'Params' => 'ARGV' } ); my @unsorted = keys %myData; my @sorted = sort { $myData{$a}{'Category'} cmp $myData{$b}{'Category'} or $myData{$a}{'File'} cmp $myData{$b}{'File'} or $myData{$a}{'Name'} cmp $myData{$b}{'Name'} } @unsorted; print join(' ', @sorted),"\n"; #prints Method2 Method4 Method3 Method1

    This works because cmp gives back 0 on equality. Which means that the 'or' doesn't short circuit and tests the next comparision.

    I dropped the 'reverse' from the sort and exchanged $a and $b of the cmp instead.

Re: Printing a Tree of Hash of hashes sorted by value
by GrandFather (Saint) on Oct 13, 2008 at 20:25 UTC

    Always use strictures (use strict; use warnings; - see The strictures, according to Seuss). Use meaningful variable names. In particular, never be tempted to use names like 'tempN'. $temp1 should be $currCat, $temp2 should be $currFile and temp3 could be $currMethod.

    You use %myData and %mydata. They should probably be the same thing. Strictures would have alerted you immediately. Correcting that and using your sample data prints:

    Functions Alpha.dat MNO Methods Beta.dat ABC

    See other replies for a better solution to your problem.


    Perl reduces RSI - it saves typing
Re: Printing a Tree of Hash of hashes sorted by value
by JavaFan (Canon) on Oct 13, 2008 at 20:04 UTC
    It'll take me too much time to figure out what your code does. But if you want to sort the hashes, I think you want:
    my %hoh = (Method1 => { ... }, Method2 => { ... }, ...); foreach my $key (sort {$hoh{$a}{Category} cmp $hoh{$b}{Category} or $hoh{$a}{File} cmp $hoh{$b}{File} or $hoh{$a}{Name} cmp $hoh{$b}{Name}} keys %hoh) { print Dump $hoh{$key} }
      Hey Java Fan,
      My apologies for the complicated code. As much as i'd like to simplify it, I have not been able to get the functionality i want.
      As mentioned earlier, my goal is to just sort the hash for print purposes. I do not care what order it is otherwise. The data i have is a collection of methods of different categories stored in different files.
      I wish to print them in ascending order of Category then ascending order of files that belong to that category followed by ascending order of Name of methods in each of those files. ie.
      Functions Alpha.dat ... MNO ... ... .... Methods Beta.dat ABC ... ... ...
      As explained earlier, I can do it, but it takes too much time to do so and I wanted to know if i can speed it up.
      I hope this explains what I wish to achieve.
      Bugz
        In which way doesn't my code do what you want?
Re: Printing a Tree of Hash of hashes sorted by value
by NetWallah (Canon) on Oct 13, 2008 at 21:28 UTC
    If your hash is really big, you could speed things up by using a Schwartzian transform sort:
    #--snip -- Hash declaration omitted -- #-- Schwartzian Transform Sort -- my @sorted= map{ $_->[3] } sort{ $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] || $a->[2] cmp $b->[2] } map{ [ @{ $myData{$_} }{qw| Category File Name | },$_] } keys %myData; print join (", ",@sorted), "\n"; #Prints: Method2, Method4, Method3, Method1
    Update:On second thoughts - this is probably a marginal gain in efficiency - array access Vs. Hash value fetch.

         Have you been high today? I see the nuns are gay! My brother yelled to me...I love you inside Ed - Benny Lava, by Buffalax