in reply to I need to sort a 2D list.

What you need to do is to put the numbers into a structure that can be sorted while keeping track of the positions of the values in the original 2D array.

A hash is ideal for this, store the array indexes as an anonymous array in the hash value. Then sort on the hash key and that will give you the rank.

This was my first attempt:

use warnings; use strict; my @total; $total[1][1] = 123; $total[2][1] = 345; $total[3][1] = 222; # hash of arrays: hash{ number } = [ i, j ] my %hash; for my $i ( 1 .. $#total ) { for my $j ( 1 .. $#{$total[$i]} ) { $hash{ $total[$i][$j] } = [ $i, $j ]; } } my @rank; my $ranking = 0; foreach my $value ( sort keys %hash ){ my ( $i, $j ) = @{$hash{ $value }}; $rank[$i][$j] = ++$ranking; } for my $i ( 1 .. $#rank ) { for my $j ( 1 .. $#{$rank[$i]} ) { print "rank[$i][$j] is $rank[$i][$j]\n"; } }
and here is the output:

rank[1][1] is 1 rank[2][1] is 3 rank[3][1] is 2
But then I realised that this won't work if a value appears more than once: the hash element is overwritten. We need a little more intelligence, here's my second attempt:
use warnings; use strict; my @total; $total[1][1] = 123; $total[2][1] = 345; $total[3][1] = 222; $total[4][1] = 222; # hash of arrays: hash{ number } = [ i, j ] my %hash; for my $i ( 1 .. $#total ) { for my $j ( 1 .. $#{$total[$i]} ) { push @{$hash{ $total[$i][$j] }}, $i, $j; } } my @rank; my $ranking = 0; foreach my $value ( sort keys %hash ){ ++$ranking; while ( my ($i, $j ) = splice @{$hash{ $value }}, 0, 2 ){ $rank[$i][$j] = $ranking; } } for my $i ( 1 .. $#rank ) { for my $j ( 1 .. $#{$rank[$i]} ) { print "rank[$i][$j] is $rank[$i][$j]\n"; } }
which gives:
rank[1][1] is 1 rank[2][1] is 3 rank[3][1] is 2 rank[4][1] is 2

As an aside, you are the second poster today I have seen starting arrays at 1 - doing this is a bit confusing, and also you still get $array[0] created automatically and set to undef. This then gives you extra values if you do something like print @array.

-- iakobski

Replies are listed 'Best First'.
Re: Re: I need to sort a 2D list.
by Anonymous Monk on May 04, 2001 at 23:01 UTC
    Thank you for the information. It will work well. However, I forgot to mention that if two value are equal, they take the next two ranks and divide by two. For example:
    $total[1][1] = 123; $total[2][1] = 345; $total[3][1] = 222; $total[4][1] = 222; rank[1][1] should be 1 rank[2][1] should be 4 rank[3][1] should be 2.5 #(3+2)/2 rank[4][1] should be 2.5 #(3+2)/2
    How can I do this?
      Well that should be easy - instead of incrementing by one each time through the loop, you need to count the number of values at that rank. Then do a separate increment before and after using the rank.

      I reckon for num values at the given rank you would need (num - 1)/2 as the post-increment and (num - postincrement) as the pre-increment, but you know what you are trying to achieve.

      -- iakobski