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

I am really struggling to sort a multi-dimensional associative array. Hopefully someone can help me out.

I have an array such as follows:

%my_array=("Key_One"=>["50","25","100","90"], "Key_Two"=>["35","18","110","72"], "Key_Three=>["100","35","99","82"] );


I am trying to sort key/values according to value for the top "X" number of values per column. For example, if I have "X" set to 2, output for the sample data above would be as follows:

Column One
Key_Three = 100
Key_One = 50

Column Two
Key_Three = 35
Key_One = 25

Column Three
Key_Two = 110
Key_One = 100

Column Four
Key_One = 90
Key_Three = 82

By the way, what is a good resource for someone that wishes to become fluent at sorting data as is required by this problem? Anyone know of a good book that covers this subject well?

Thanks very much in advance.

Replies are listed 'Best First'.
Re: Array Sorting Trouble
by Abigail-II (Bishop) on Oct 02, 2002 at 13:28 UTC
    use strict; use warnings; my $X = @ARGV ? shift : 2; my %array = ( Key_One => [qw / 50 25 100 90/], Key_Two => [qw / 35 18 110 72/], Key_Three => [qw /100 35 99 82/], ); my @names = qw /One Two Three Four/; for (my $i = 0; $i < @names; $i ++) { my @keys = sort {$array {$b} [$i] <=> $array {$a} [$i]} keys %arra +y; print "Column $names[$i]\n"; foreach my $key (@keys [0 .. $X - 1]) { printf "%10s = %3d\n" => $key, $array {$key} [$i] } } __END__ Column One Key_Three = 100 Key_One = 50 Column Two Key_Three = 35 Key_One = 25 Column Three Key_Two = 110 Key_One = 100 Column Four Key_One = 90 Key_Three = 82

    Abigail

Re: Array Sorting Trouble
by BrowserUk (Patriarch) on Oct 02, 2002 at 15:01 UTC

    I can't help but think that the biggest problem is that your using the wrong data structure. From your output requirements, it seems like you need an array of hashes, rather the a hash of arrays.

    To that end, the first loop will transform your described structure into what I think would be more appropriate. I've dumped the output to show how I think the data would be better structured.

    Of course, if you have the option, and no other code is dependant upon the existing structure, it would be better to build it this way in the first place rather than needing to transform it.

    When the data is structured this way, printing the output becomes almost trivial and requires only a simple sort method.

    #! perl -sw use strict; use Data::Dumper; local $\=$/; $::X = 2 unless $::X; my %array=( Key_One =>[ 50,25,100,90], Key_Two =>[ 35,18,110,72], Key_Three =>[100,35, 99,82], ); my @columns; for my $i (0..3) { $columns[$i]->{@{$array{$_}}[$i]} = $_ for keys %array; } print Dumper(\@columns); my @names = qw(One Two Three Four); for my $column (@columns) { print $/,'Column ', shift@names; print "$column->{$_} = $_" for (sort{$b <=> $a} keys %$column)[0 . +. $::X-1]; } __DATA__ c:\test>202251 -X=2 $VAR1 = [ { '50' => 'Key_One', '35' => 'Key_Two', '100' => 'Key_Three' }, { '25' => 'Key_One', '18' => 'Key_Two', '35' => 'Key_Three' }, { '110' => 'Key_Two', '99' => 'Key_Three', '100' => 'Key_One' }, { '72' => 'Key_Two', '90' => 'Key_One', '82' => 'Key_Three' } ]; Column One Key_Three = 100 Key_One = 50 Column Two Key_Three = 35 Key_One = 25 Column Three Key_Two = 110 Key_One = 100 Column Four Key_One = 90 Key_Three = 82 c:\test>

    Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!
Re: Array Sorting Trouble
by fglock (Vicar) on Oct 02, 2002 at 13:30 UTC
    %my_array=(Key_One=>["50","25","100","90"], Key_Two=>["35","18","110","72"], Key_Three=>["100","35","99","82"] ); $a_key = each %my_array; for $column (0 .. $#{$my_array{$a_key}}) { @a = sort { $b <=> $a } map { $my_array{$_}[$column] } keys %my_array; print $column+1, ": @a[0..1] \n"; }

    output:

    1: 100 50 2: 35 25 3: 110 100 4: 90 82

    note on "each" function: When called in scalar context, returns only the key for the next element in the hash.

      Due to your usage of map, you lose the information which keys are in the top $X. You have left are the values, but the example output did show the key names.

      Abigail

        You are right. Here is a version that saves the keys:

        %my_array=(Key_One=>["50","25","100","90"], Key_Two=>["35","18","110","72"], Key_Three=>["100","35","99","82"] ); $a_key = each %my_array; for $column (0 .. $#{$my_array{$a_key}}) { @a = sort { $b->[0] <=> $a->[0] } map { [$my_array{$_}[$column], $_] } keys %my_array; print $column+1, ": $a[0][1]=$a[0][0], $a[1][1]=$a[1][0] \n"; }