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

I need some advice on a classic sorting problem. Below is a little example of what my data looks like.

Data.txt 1.000 64.103 2840.0 1.000 42.735 2840.0 1.000 32.051 2840.0 1.050 64.103 3280.0 1.050 42.735 3280.0 1.050 32.051 3280.0 1.100 64.103 3720.0 1.100 42.735 3720.0 1.100 32.051 3720.0

I'd like my data to be first sorted by the second column then by the first column. It should look like the below example.

1.000 64.103 2840.0 1.050 64.103 3280.0 1.100 64.103 3720.0 1.000 42.735 2840.0 1.050 42.735 3280.0 1.100 42.735 3720.0 1.050 32.051 3280.0 1.000 32.051 2840.0 1.100 32.051 3720.0

I started using nested foreach loops but it started getting a little hairy, so I figured I'd ask the wise Monks. Thank you in advance for your time!

Replies are listed 'Best First'.
Re: Classic Sort Algorithm
by AnomalousMonk (Archbishop) on Jun 20, 2011 at 22:45 UTC

    This is a FAQ: How do I sort an array by (anything)?. (See also  perldoc perlfaq4) You want the

    @sorted = sort { field1($a) <=> field1($b) || field2($a) cmp field2($b) || field3($a) cmp field3($b) } @data;

    'paradigm' for multi-key sorting.

      I'm not sure what it means when it refers to field1($a). When I insert this into my code I get a Undefined subroutine &main::field1 called at error.
        I'm not sure what it means when it refers to field1($a). When I insert this into my code I get a Undefined subroutine &main::field1 called at error.
        Yes. It was left to the reader, you, to define a method that extracts field1 from a line of data. Maybe calling the function get_field1() would have been clearer.

        Note that when perl sorts a group of things it has to compare each thing to the other things in the group many times. When comparing things perl assigns two of the things to the global variables $a and $b.

Re: Classic Sort Algorithm
by Perlbotics (Archbishop) on Jun 20, 2011 at 22:46 UTC

    use strict; use warnings; my @list = ( [qw(1.000 64.103 2840.0)], [qw(1.000 42.735 2840.0)], [qw(1.000 32.051 2840.0)], [qw(1.050 64.103 3280.0)], [qw(1.050 42.735 3280.0)], [qw(1.050 32.051 3280.0)], [qw(1.100 64.103 3720.0)], [qw(1.100 42.735 3720.0)], [qw(1.100 32.051 3720.0)] ); my @sorted = sort { $b->[1] <=> $a->[1] # 2nd col (biggest first) || $a->[0] <=> $b->[0] # 1st col (lowest first) } @list; foreach(@sorted) { print join("\t", @{$_}),"\n"; } __END__ Output: 1.000 64.103 2840.0 1.050 64.103 3280.0 1.100 64.103 3720.0 1.000 42.735 2840.0 1.050 42.735 3280.0 1.100 42.735 3720.0 1.000 32.051 2840.0 1.050 32.051 3280.0 1.100 32.051 3720.0

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Classic Sort Algorithm
by aquarium (Curate) on Jun 21, 2011 at 04:36 UTC
    i'm truly lazy with these sorts of things...rather than reinvent the wheel at linux/unix/osx/cygwin prompt use command like
    sort -n -r -k2,1 your_input_file
    the hardest line to type correctly is: stty erase ^H
Re: Classic Sort Algorithm
by salva (Canon) on Jun 21, 2011 at 08:24 UTC
    check Sort::Key::Multi:
    use Sort::Key::Multi qw(n2_keysort); my @data = <>; my @sorted = n2_keysort { (split)[1, 0] } @data; print @sorted;

    Update: Perlbotics has pointed me out that the OP probably wants the data to be sorted by the second column in descending order and then by the first column in ascending order, so...

    use Sort::Key::Multi qw(rnn_keysort); my @data = <>; my @sorted = rnn_keysort { (split)[1, 0] } @data; print @sorted;

    Update 2: s/sortkey/keysort/g;

Re: Classic Sort Algorithm
by Cristoforo (Curate) on Jun 21, 2011 at 00:33 UTC
    #!/usr/bin/perl use strict; use warnings; my @x = map {$_->[0]} sort {$b->[2] <=> $a->[2] || $a->[1] <=> $b->[1] } map {[$_, split]} grep {/\S/} <DATA>; for my $i (0 .. $#x) { print $x[$i]; print "\n" if ($i + 1) % 3 == 0; } __DATA__ 1.000 64.103 2840.0 1.000 42.735 2840.0 1.000 32.051 2840.0 1.050 64.103 3280.0 1.050 42.735 3280.0 1.050 32.051 3280.0 1.100 64.103 3720.0 1.100 42.735 3720.0 1.100 32.051 3720.0

    prints

    1.000 64.103 2840.0 1.050 64.103 3280.0 1.100 64.103 3720.0 1.000 42.735 2840.0 1.050 42.735 3280.0 1.100 42.735 3720.0 1.000 32.051 2840.0 1.050 32.051 3280.0 1.100 32.051 3720.0