Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Sorting an array of hashes

by Boghog (Novice)
on Mar 26, 2008 at 08:19 UTC ( #676326=perlquestion: print w/ replies, xml ) Need Help??
Boghog has asked for the wisdom of the Perl Monks concerning the following question:

Greetings Monks,
I wanted to sort an array of hashes based on the size of each hash (i.e., the number of the key/value pairs in each hash) using something like:
#!/usr/bin/perl use strict; my @AoH = ( {a => 1, b => 2, c => 3}, {a => 1, b => 2}, {a => 1, b => 2, c => 3, d => 4}, {a => 1} ); my @AoH_sorted = sort { keys(%{$AoH[$b]}) <=> keys(%{$AoH[$a]}) } @AoH +; foreach my $i (0 .. $#AoH_sorted) { print "$i\t"; if (exists $AoH_sorted[$i]) { my %hash = %{$AoH_sorted[$i]}; foreach my $key (keys %hash) { print "$key\t"; } } print "\n"; }
However the above script produces the following output which obviously has not been sorted:
0 c a b 1 a b 2 c a b d 3 a
Any ideas on how to get this sort to work? Thanks!

Comment on Sorting an array of hashes
Select or Download Code
Re: Sorting an array of hashes
by haoess (Curate) on Mar 26, 2008 at 08:27 UTC

    You should use warnings;, this will give you the hint ;-)

    -- Frank

Re: Sorting an array of hashes
by stiller (Friar) on Mar 26, 2008 at 08:38 UTC
    You should try use diagnostics; when things get tough, and use warnings; unless you really know you wont need it (you seldom can know that), but the hints use warnings and diagnostics would give here isn't trivial to understand.
    You are just overdoing your sort criteria:
    use strict; use warnings; my @AoH = ( {a => 1, b => 2, c => 3}, {a => 1, b => 2}, {a => 1, b => 2, c => 3, d => 4}, {a => 1} ); my @AoH_sorted = sort { (keys(%{$b})) <=> (keys(%{$a})) } @AoH; foreach my $i (0 .. $#AoH_sorted) { print "$i\t"; # if (exists $AoH_sorted[$i]) { # no need to check if exist when usi +ng foreach my %hash = %{$AoH_sorted[$i]}; foreach my $key (keys %hash) { print "$key\t"; } # } print "\n"; }

    gives:
    0 c a b d 1 c a b 2 a b 3 a

      It took me a bit to understand the error/solution myself here. $a and $b are set to elements of @AoH, not indexes of @AoH, so there is no need to look up the element as in %{$AoH[$b]}, you just directly dereference the hash reference directly as %{$b}, correct?

      I agree on the use warnings; as that 'Use of reference "HASH(########)" as array index at myscript.pl' saves me from my own novice-ness constantly.

      # no need to check if exist when using foreach
      #! perl use warnings; use strict; my @x = (1,2,3,4,5); delete $x[2]; for (0..4) { print $x[$_] }
        Actually, in that case you need to check if the value is defined. The element still exists, and checking for existens doesn't buy you anything.
Re: Sorting an array of hashes
by admiral_grinder (Pilgrim) on Mar 26, 2008 at 12:49 UTC
    Personally, I would simplify this more by removing the dependence on the $i variable. It is fine to have $i in the code for outputting, but if you want to remove it later you can be sure not to introduce bugs:
    my $i = 0; foreach my $hash ( @AoH_sorted ) { # Swap these for start with 0 $i++; print "$i\t"; foreach my $key ( keys %{$hash} ) { print "$key\t"; } print "\n"; }
Re: Sorting an array of hashes
by oko1 (Deacon) on Mar 26, 2008 at 16:46 UTC

    ++ on '-w', of course. You could make the sorting happen more-or-less the way you were thinking about it - but it would be fragile, to say the least:

    #!/usr/bin/perl -w use strict; my @AoH = ( {a => 1, b => 2, c => 3}, {a => 1, b => 2}, {a => 1, b => 2, c => 3, d => 4}, {a => 1} ); sub mysort { # Awful abuse of Perl, here... (my $A = %$a) =~ s#/.*##; (my $B = %$b) =~ s#/.*##; $B <=> $A; } my $count = 0; print join("\t", $count++, keys %$_), "\n" for sort mysort @AoH;
    Here's something that's a bit cleaner - at least to my mind - and certainly more robust:
    #!/usr/bin/perl -w use strict; my @AoH = ( {a => 1, b => 2, c => 3}, {a => 1, b => 2}, {a => 1, b => 2, c => 3, d => 4}, {a => 1} ); my $count = 0; print join("\t", $count++, keys(%{$AoH[$_]})), "\n" for sort { keys(%{$AoH[$b]}) <=> keys(%{$AoH[$a]}) } 0 .. $#AoH;

    Update: Removed the unnecessary 'scalar' from the 'my $A = scalar %$a' in the first code example.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://676326]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (5)
As of 2014-07-24 02:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (156 votes), past polls