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

Hi, I have read the article titled "How do I sort a multidimensional arrays?" and I learnt how to sort a multidimensional array according to the value of the specific element (ex: second element).
@array = ([8,9,10], [4,5,6], [7,8,9]); @sorted = sort { $a->[1] <=> $b->[1] } @array
but What about to sort a multidimensional array using the elements of @{$array[1]}? Is that possible?
@unsortedarray = [8,9,10], [6,5,4], [7,8,9]; @sortedarray = [10,9,8], [4,5,6], [9,8,7];

Replies are listed 'Best First'.
Re: sorting multidimensional arrays
by ikegami (Patriarch) on Mar 15, 2005 at 19:33 UTC

    It's convenient to mirror the array (switch rows for columns), sort it, then mirror it back.

    use strict; use warnings; sub mirror { my $num_rows = @_; my $num_cols = @{$_[0]}; return ( map { my $col = $_; [ map { my $row = $_; $_[$row][$col] } 0..$num_rows-1 ] } 0..$num_cols-1 ); } my @unsorted_array = ([8,9,10], [6,5,4], [7,8,9]); my @sorted_array = mirror sort { $a->[1] <=> $b->[1] } mirror @unsorted_array; require Data::Dumper; print(Data::Dumper::Dumper(\@sorted_array)); __END__ Original ======== [ 8, 9, 10 ] [ 6, 5, 4 ] [ 7, 8, 9 ] After Bottom Mirror =================== [ 8, 6, 7 ] [ 9, 5, 8 ] [ 10, 4, 9 ] After Sort ========== [ 10, 4, 9 ] [ 9, 5, 8 ] [ 8, 6, 7 ] After Top Mirror ================ [ 10, 9, 8 ] [ 4, 5, 6 ] [ 9, 8, 7 ]
Re: sorting multidimensional arrays
by friedo (Prior) on Mar 15, 2005 at 19:30 UTC
    It looks like you want to reverse each second-level array while keeping them in place in the first-level array. In that case, you can do something like this:

    my @unsorted = ( [8,9,10], [6,5,4], [7,8,9] ); foreach my $inner(@unsorted) { $inner = [ reverse @{ $inner } ]; }

    This works because of the special aliasing nature of a for-loop. The loop variable $inner is an alias for each array reference, so changing it results in changing the array reference inside @unsorted.

      I think he wanted to sort them in descending order, not reverse them. It just happens that with the example data he gave, the two are the same.

      So I'll just note that substituting sort { $b <=> $a} for the reverse in your example works, too.

      Oh, and if he didn't want the original data affected:

      my @sorted = map {[sort { $b <=> $a } @$_]} @unsorted;

      Caution: Contents may have been coded under pressure.
Re: sorting multidimensional arrays
by ikegami (Patriarch) on Mar 15, 2005 at 22:23 UTC

    Here's an optimization of my previous answer.

    use strict; use warnings; my @unsorted_array = ([8,9,10], [6,5,4], [7,8,9]); my @order_lookup; { my $row = $unsorted_array[1]; @order_lookup = sort { $row->[$a] <=> $row->[$b] } 0..$#$row; } my @sorted_array; { my $num_rows = @unsorted_array; my $num_cols = @{$unsorted_array[0]}; foreach my $row (@unsorted_array) { push(@sorted_array, [ map { $row->[$_] } @order_lookup ]); } } require Data::Dumper; print(Data::Dumper::Dumper(\@sorted_array));
Re: sorting multidimensional arrays
by tlm (Prior) on Mar 16, 2005 at 02:56 UTC

    Note: I managed to screw up again by posting this in the wrong place. Two gaffes in a day. I hope the gods will forgive me.


    If you seek a one-liner answer, I don't know what it is, but if you just want to get the job done, then simply transpose the matrix, apply the earlier solution (for ordering according to the n-th row elements), and then transpose back. I.e., for the general case of a non-square matrix,

    sub transpose { map { my $j = $_; [ map $_[$_][$j], 0..$#_ ] } 0..$#{$_[0]}; } my @array = ([8, 9, 10], [6, 5, 4], [7, 8, 9]); my @sorted = transpose sort { $a->[1] <=> $b->[1] } transpose @array; 1; # target for c in DB __END__ DB<1> x @sorted 0 ARRAY(0x8359764) 0 10 1 9 2 8 1 ARRAY(0x84bdcb0) 0 4 1 5 2 6 2 ARRAY(0x84bdc8c) 0 9 1 8 2 7

    the lowliest monk