views:

239

answers:

3
@aoaoh;

$aoaoh[0][0]{21} = 31;
$aoaoh[0][0]{22} = 31;
$aoaoh[0][0]{23} = 17;

for $k (0 .. $#aoaoh) {
    for $i(0.. $#aoaoh) {
        for $val (keys %{$aoaoh[$i][$k]}) {
            print "$val=$aoaoh[$i][$k]{$val}\n";
        }
    }
}

The output is:

    22=31
    21=31
    23=17

but i expect it to be

    21=31
    22=31
    23=17

Please tell me where is this wrong.

Also how do I sort the values so that i get the output as

    23=17 
    22=31
    21=31 (if 2 keys have same value then key with higher value come first)
+1  A: 

Sounds like you want:

for $val (sort keys %{$aoaoh[$i][$k]}) {

and:

for $val (reverse sort keys %{$aoaoh[$i][$k]}) {

Although from your comment it looks like you don't want a pure reverse sort. You want to create your own sort function:

for $val (sort {$aoaoh[$i][$k]->{$a} <=> $aoaoh[$i][$k]->{$b} || $a <=> $b} keys %{$aoaoh[$i][$k]}) {
Epsilon Prime
I think the second half of his question was how to sort by values, rather than keys. It was just coincidence that this was the same as reverse-key order.
Will
yes.. Will got me.. i want to sort it on values and then if 2 values then sort those 2 in descending order...
srk
could you please tell me how to do that ?
srk
Hey thanks Epsilon..
srk
Yeah, put an example sort function in my answer.
Epsilon Prime
Hey.. i have another doubt..can we just sort @aoaoh without using for loop's.. In a case where i am not printing them and just want to save it in sorted order.. Do i need to use loops ?
srk
@srk: no. Just write a custom sort function. See http://perldoc.perl.org/functions/sort.html.
Ether
A: 

It sounds like you are expecting hashes to return their keys and values in the order defined, where there is no such guarantee in perl.

You can either implement a more complex data structure that handles some of the ordering for you, or put some more sorting logic in your display (instead of just looping), or load up a module to support ordered hashes, such as Tie::Hash::Indexed.

I would expect the Tie::Hash::Indexed implementation to look like so:

my @aoaoh;

use Tie::Hash::Indexed;
tie my %hash, 'Tie::Hash::Indexed';
$aoaoh[0][0] = \%hash;

$aoaoh[0][0]{21} = 31;
$aoaoh[0][0]{22} = 31;
$aoaoh[0][0]{23} = 17;

for $k (0 .. $#aoaoh) {
    for $i(0.. $#aoaoh) {
        for $val (keys %{$aoaoh[$i][$k]}) {
            print "$val=$aoaoh[$i][$k]{$val}\n";
        }
    }
}
kbenson
I have an array of array of hashes.. I want the hashes in the inner array to be sorted by their values first and when the values are same, reverse sorted by their keys..
srk
for $val (sort {$aoaoh[$i][$k]->{$a} <=> $aoaoh[$i][$k]->{$b} || $b <=> $a} keys %{$aoaoh[$i][$k]}) Solution from Epsilon.. This is what i want.. but i want to avoid loops so what is an alternate for this...
srk
A: 

I answered on of the other two exact same questions by this user, but it looks like this one is going to win so I'm answering here too.

From perlfaq4's answer to How do I sort an array by anything?


Supply a comparison function to sort() (described in sort in perlfunc):

@list = sort { $a <=> $b } @list;

The default sort function is cmp, string comparison, which would sort (1, 2, 10) into (1, 10, 2). <=>, used above, is the numerical comparison operator.

If you have a complicated function needed to pull out the part you want to sort on, then don't do it inside the sort function. Pull it out first, because the sort BLOCK can be called many times for the same element. Here's an example of how to pull out the first word after the first number on each item, and then sort those words case-insensitively.

@idx = ();
for (@data) {
    ($item) = /\d+\s*(\S+)/;
    push @idx, uc($item);
    }
@sorted = @data[ sort { $idx[$a] cmp $idx[$b] } 0 .. $#idx ];

which could also be written this way, using a trick that's come to be known as the Schwartzian Transform:

@sorted = map  { $_->[0] }
    sort { $a->[1] cmp $b->[1] }
    map  { [ $_, uc( (/\d+\s*(\S+)/)[0]) ] } @data;

If you need to sort on several fields, the following paradigm is useful.

@sorted = sort {
    field1($a) <=> field1($b) ||
    field2($a) cmp field2($b) ||
    field3($a) cmp field3($b)
    } @data;

This can be conveniently combined with precalculation of keys as given above.

See the sort article in the "Far More Than You Ever Wanted To Know" collection in http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz for more about this approach.

See also the question later in perlfaq4 on sorting hashes.

brian d foy