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

Hi, if I have an array of array refs, for example:
my @un_sorted = ([3,4,23,4,5],[3,4,56,2,4],[2,3,43,5,3],[2,3,43,6],[3 +,4]);
How do I sort by the first column, then 2nd, 3rd, etc. In my situation, I actually don't know how many columns, and each row may have different sizes. I'd like the result to be:
2,3,43,5,3 2,3,43,6 3,4,23,4,5 3,4,56,2,4 3,4

Replies are listed 'Best First'.
Re: sort by a multiple columns
by Roy Johnson (Monsignor) on Sep 08, 2004 at 19:58 UTC
    Here's a Schwartzian Transform solution (assuming your numbers don't exceed 12 digits):
    print join "\n", map { join ',', @{$_->[0]} } sort { $a->[1] cmp $b->[1] } map { my $x; for(@$_){$x.=sprintf("%012d",$_)}; [$_,$x] } @un_sorted;

    Caution: Contents may have been coded under pressure.
Re: sort by a multiple columns
by Roy Johnson (Monsignor) on Sep 08, 2004 at 19:07 UTC
    print join "\n", map { join ',', @$_ } sort { for (0..($#{$a}<$#{$b}?$#{$b}:$#{$a})) { return $a->[$_] <=> $b->[$_] || $a->[$_] cmp $b->[$_] || next } } @un_sorted;
    The mess on the for line determines how many elements there are in the longer of the two arrays being compared. The comparison is by number, then by string (so that null sorts before 0). If there's no difference either way, we continue through the loop. If there is a difference, it returns.

    Update: I came up with a better way of handling different-length arrays below.


    Caution: Contents may have been coded under pressure.

      You need to handle the case when comparing a shorter array with a longer one:

      #! perl -slw use strict; my @un_sorted = ([3,4,23,4,5],[3,4,56,2,4],[2,3,43,5,3],[2,3,43,6],[3, +4]); print join "\n", map{ join ',', @$_ } sort { for( 0 .. ( $#{$a} < $#{$b} ? $#{$b} : $#{$a} ) ) { return ( $a->[$_] || 0 ) <=> ( $b->[$_] || 0 ) || next } } @un_sorted; __END__ P:\test>before Use of uninitialized value in numeric comparison (<=>) at P:\test\junk +.pl line 10. 2,3,43,5,3 2,3,43,6 3,4 3,4,23,4,5 3,4,56,2,4 P:\test>after 2,3,43,5,3 2,3,43,6 3,4 3,4,23,4,5 3,4,56,2,4

      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
        I'm sorry, but neither you nor Roy Johnson follow the OP insofar as he would like undefined array elements to be treated like infinity rather than zero, that is, he says he wants
        3,4,23,4,5 3,4,56,2,4 3,4
        instead of
        3,4 3,4,23,4,5 3,4,56,2,4
        Might just have been a typo though, as it's quite unusual.
        CombatSquirrel.

        Entropy is the tendency of everything going to hell.
        You're not really handling that case, you're just silencing a warning. Zero and undef still compare as equal. Note the use of cmp to differentiate zero from undef (I may have added that while you were typing your reply).

        Caution: Contents may have been coded under pressure.
Re: sort by a multiple columns
by Pragma (Scribe) on Sep 08, 2004 at 21:58 UTC
    Avoiding the Schwartzian:
    my @sorted = map [ map int, /.{12}/g ], sort map { join '', map { sprintf "%012d", $_ } @$_ } @un_sorted;
Re: sort by a multiple columns
by BrowserUk (Patriarch) on Sep 08, 2004 at 22:19 UTC

    #! perl -slw use strict; my @un_sorted = ([3,4,23,4,5],[3,4,56,2,4],[2,3,43,5,3],[2,3,43,6], [3 +,4] ); print join "\n", map{ join ',', @$_ } sort { for( 0 .. ( $#{$a} < $#{$b} ? $#{$b} : $#{$a} ) ) { return ( defined( $a->[ $_ ] ) ? $a->[ $_ ] : 1e308 ) <=> ( defined( $b->[ $_ ] ) ? $b->[ $_ ] : 1e308 ) || next; } return 0; } @un_sorted; __END__ P:\test>junk 2,3,43,5,3 2,3,43,6 3,4,23,4,5 3,4,56,2,4 3,4

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
Re: sort by a multiple columns
by CombatSquirrel (Hermit) on Sep 08, 2004 at 22:03 UTC
    This is really ugly (maybe someone could improve it with pack/unpack), but it reproduces your desired output:
    #!perl use strict; use warnings; my @un_sorted = ( [3, 4, 23, 4, 5], [3, 4, 56, 2, 4], [2, 3, 43, 5, 3], [2, 3, 43, 6], [3, 4] ); my $end = chr(1e6); print join "\n", map { join ',', @$_ } map { splice @$_, $#_; $_ } map { [(map(ord, split('', $_)))]} sort map { join('', map chr, @$_) . $end } @un_sorted;
    It's a GRT (kinda, probably not the best performance, though). The trick with the end marker is especially ugly, but without it the output would be wrong.
    Hope this helped.
    CombatSquirrel.

    Entropy is the tendency of everything going to hell.
Re: sort by a multiple columns
by dougX (Initiate) on Sep 08, 2004 at 22:57 UTC
    How is this?
    my @arr4 = sort { $a->[0] cmp $b->[0] } @un_sorted; print "Mutli-Dimensional Array\n"; for my $i ( 0 .. $#arr4) { for my $j (0 .. $#{$arr4[$i]}) { print "$arr4[$i][$j]\t"; } print "\n"; }

    Edited by Chady -- replade <pre> with <code>

      Bad on several fronts: You are doing an alphabetical comparison of the first element only; you didn't use <code> tags around your code; and you misspelled "multi". It's generally more Perlescent to iterate over the arrays rather than their indexes.

      I don't mean to be discouraging; on the contrary, I hope you will find this assessment helpful. Don't give up. Welcome to the Monestary.


      Caution: Contents may have been coded under pressure.
Re: sort by a multiple columns
by mayhem (Hermit) on Sep 09, 2004 at 17:03 UTC
    Simple and works for variety of elements, to the best of knowledge
    my @un_sorted = ([3,4,23,4,5],[3,4,56,2,4],[2,3,43,5,3],[2,3,43,6],[3, +4],[1,7,99,204],[1],[4,12],[110,99], [1111,19], [99]); @un_sorted = sort { $a->[0] <=> $b->[0] } @un_sorted;