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

Dear monks,

this is likely basic stuff and most probably just my searching skills eluding me, so please bear with me. I have 3 arrays. One of these arrays has simple integers and I wish to sort that array descendingly. So far so good. However, the other 2 arrays need to be sorted in exactly the same manner as the first array. In other words, if item 116 moves to 211, the same needs to happen for item 116 in the other 2 arrays. I know that I should likely turn to map for this, but even after going through the documentation a number of times I can't seem to figure out exactly how. Another method I thought of was to create a hash based on the sort where the keys are the old positions and the values the new ones, possibly combining this approach with map(perhaps that's the whole idea of map...beats me). Any insights?


Remember rule one...
  • Comment on sorting multiple arrays on the criteria of one array

Replies are listed 'Best First'.
Re: sorting multiple arrays on the criteria of one array
by BrowserUk (Patriarch) on Jul 27, 2005 at 22:15 UTC

    Sort the reference array, but record the ordered indexes, then use those to reorder the other two arrays:

    #! perl -slw use strict; my @a1 = reverse 1 .. 10; my @a2 = 'a' .. 'j'; my @a3 = 'A' .. 'J'; ## sort the array but record the ordered indexes. my @order = sort{ $a1[ $a ] <=> $a1[ $b ] } @0 .. $#a1; @a1 = @a1[ @order ]; @a2 = @a2[ @order ]; @a3 = @a3[ @order ]; print "@$_" for \( @a1, @a2, @a3 ); __END__ P:\test>junk3 1 2 3 4 5 6 7 8 9 10 j i h g f e d c b a J I H G F E D C B A

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
Re: sorting multiple arrays on the criteria of one array
by friedo (Prior) on Jul 27, 2005 at 21:42 UTC
    Instead of using three separate arrays, perhaps you could solve your problem by using an array of hashes. For example:

    my @data = ( { number => 42, foo => "blah", bar => "hello" }, { number => 67, foo => "red", bar => "green" } ); @data = sort { $a->{number} <=> $b->{number} } @data;

    This would sort @data on the number key of each hash, and the foos and bars would go with them.

Re: sorting multiple arrays on the criteria of one array
by AReed (Pilgrim) on Jul 27, 2005 at 22:09 UTC
    The trick is to sort the indexes of the items in the first array into order based on the values in that array. Then you can use the sorted indexes to index each of your arrays.
    #!/usr/bin/perl use warnings; use strict; my @data = (5, 2, 7, 0, 4); my @moredata = qw/five two seven zero four/; my @idx = sort {$data[$b] <=> $data[$a]} (0..$#data); for my $idx (@idx) { print "$data[$idx] $moredata[$idx]\n"; }
Re: sorting multiple arrays on the criteria of one array
by borisz (Canon) on Jul 27, 2005 at 21:57 UTC
    Here is one way.
    my @a1 = qw/1 4 6 8 7 5/; my @a2 = qw/a b c d e f/; my @a3 = qw/g h i j k l/; my $c = 0; map { ( $a1[ $c ], $a2[ $c ], $a3[ $c ] ) = @$_; $c++; () } sort { $a->[0] <=> $b->[0] } map { [ $a1[$_], $a2[$_], $a3[$_] ] } ( 0 .. $#a1 );
    Boris
Re: sorting multiple arrays on the criteria of one array
by GrandFather (Saint) on Jul 27, 2005 at 21:45 UTC

    If the arrays are that strongly related then perhaps you should use and array of arrays. If you have an array of arrays then the Schwartzian Transform (see this thread) is the trick to solve your problem.


    Perl is Huffman encoded by design.
Re: sorting multiple arrays on the criteria of one array
by Pied (Monk) on Jul 28, 2005 at 02:51 UTC
    Here's mine, using the Schwartzian transform, recently learned :)
    use strict; my @lA=reverse(1..9); my @lB=1..9; my @lC="a".."i"; sub trimult{ my ($l1,$l2,$l3)= @_; #list refs my @l; #temp list push @l,[$$l1[$_],$$l2[$_],$$l3[$_]] foreach (0..(scalar(@{$l1})-1)) +; #an array of each 3-uplet @l=map {$_->[0]} sort{$a->[1] <=> $b->[1]} map {[$_,$_->[0]]} @l; # S.Transform: sorting according to the first + elements (ie array l1) my $t=0; # I'm sure there is a perlvar that equals 0 ($| maybe?), bu +t this aint an obfu contest :) ($$l1[$t],$$l2[$t],$$l3[$t++])=@{$_} foreach(@l); # so @l is an array of array. So I can cast it's elements type as arr +ays @{likethis} and the assign them to my vars in a list context. } #end of procedure print "@lA, @lB, @lC\n"; #print &trimult (\@lA,\@lB,\@lC);#sort print "@lA, @lB, @lC\n"; #print and enjoy.


    Hope you enjoy!

    P!