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

I am having a little trouble with sorting a hash of hashes of an array. I needed to print out the data sorted by the value of a certain array element that is contained in a hash that is contained in another hash. I was able to get the result I was looking for but I know there must be a better or at least a more perlish way of doing it. The actual data structure is much larger than it appears below but the following code is sufficient to describe the issue. Any ideas would be greatly appreciated.
#!/usr/bin/perl use strict; my %hash; $hash{cust1}{current}[0] = 13; $hash{cust2}{current}[0] = 110; $hash{cust3}{current}[0] = 10; $hash{cust4}{current}[0] = 102; $hash{cust5}{current}[0] = 108; $hash{cust6}{current}[0] = 130; $hash{cust7}{current}[0] = 10; $hash{cust8}{current}[0] = 107; $hash{cust9}{current}[0] = 5; $hash{cust10}{current}[0] = 210; $hash{cust11}{current}[0] = 1; $hash{cust12}{current}[0] = 160; $hash{cust13}{current}[0] = 16; my @sorted; for my $x(keys %hash) { push @sorted, "$hash{$x}{current}[0]|$x"; } for my $x(sort sort_num @sorted) { my @key = split(/\|/,$x); print "$key[1] - $hash{$key[1]}{current}[0]\n"; } exit; sub sort_num { return $a <=> $b; }

Replies are listed 'Best First'.
Re: Help with Sorting
by dragonchild (Archbishop) on Jul 13, 2004 at 19:10 UTC
    my @sorted_keys = sort { $hash{$a}{current}[0] <=> $hash{$b}{current}[0] } keys %hash;

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

      Thanks, that's great!
Re: Help with Sorting
by jeffa (Bishop) on Jul 13, 2004 at 19:25 UTC

    I fail to see any benefit in the structure of your data structure. If you have keys with names that contain consecutive intergers, then that is a big red flag saying you might need an array instead. Try this instead:

    use Data::Dumper; my $customers = [ map { { current => [ $_ ] } } (13, 110, 10, 102, 108, 130, 10, 107, 5, 210, 1, 160, 16) ]; print Dumper $customers;
    Now you have a simpler data structure that should provide the same functionality. If you need the value for key 'cust9' then you use index 8 instead:
    print $customer->[8]->{current}->[0];
    Sorting complex datastructures is always a little tricky, but we have a more simplified datastructure to work with now:
    my @sorted = sort { $a->{current}->[0] <=> $b->{current}->[0] } @$customer; print Dumper \@sorted;
    Be sure to use Data::Dumper often and liberally to observe what you datastructure really looks like, and use an array when you have consecutive indices.

    UPDATE: yes, i read that. I really do wish that you would have stated that 'cust1' was just a bogus key name. However, i still stand by my recommendation, unless you are doing more lookups than sorts. Leaving out code is fine, but don't leave out the details, please. "The actual data structure is much larger than it appears below but the following code is sufficient to describe the issue" in no way implies that "The keys of the top level hash are our actual customer names which I did not feel should be listed in this forum."

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      As I stated in the original post, "The actual data structure is much larger than it appears below but the following code is sufficient to describe the issue." The keys of the top level hash are our actual customer names which I did not feel should be listed in this forum. The second level hash actually contains 17 different keys and the array has 32 elements. I do apreciate your input though.
Re: Help with Sorting
by ysth (Canon) on Jul 13, 2004 at 19:30 UTC
    Another way:
    sub sort_curr0($$) { $hash{$_[0]}{current}[0] <=> $hash{$_[1]}{current +}[0] } my @sorted_keys = sort sort_curr0 keys %hash; print "$_ => $hash{$_}{current}[0]\n" for @sorted_keys;
    BTW, use warnings is probably more important than use strict. But go ahead and do both.
Re: Help with Sorting
by davidj (Priest) on Jul 13, 2004 at 19:13 UTC
    The following will do it:

    #!/usr/bin/perl use strict; my %hash; $hash{cust1}{current}[0] = 13; $hash{cust2}{current}[0] = 110; $hash{cust3}{current}[0] = 10; $hash{cust4}{current}[0] = 102; $hash{cust5}{current}[0] = 108; $hash{cust6}{current}[0] = 130; $hash{cust7}{current}[0] = 10; $hash{cust8}{current}[0] = 107; $hash{cust9}{current}[0] = 5; $hash{cust10}{current}[0] = 210; $hash{cust11}{current}[0] = 1; $hash{cust12}{current}[0] = 160; $hash{cust13}{current}[0] = 16; my @sorted = sort { $hash{$a}->{current}->[0] <=> $hash{$b}->{current} +->[0] } keys %hash; print "sorted = @sorted\n";
    hope this helps,

    davidj