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

I have the following array:
@Array[0] = "1,5"; @Array[1] = "6,2"; @Array[2] = "3,4"; @Array[3] = "3,9"; @Array[4] = "8,1";
I would like to sort it with these rules:
* Element 1 of string should be Low to High * Element 2 of string should be High to Low
Thus you would get this as a result:
@Array[0] = "1,5"; @Array[3] = "3,9"; @Array[2] = "3,4"; @Array[1] = "6,2"; @Array[4] = "8,1";
I know basic sorting but this goes too deep for me. In addition, is it bad to use the array to put strings with two 'artifical' elements. Is there another way to do it more efficient like:
$array->[0]->x=1; $array->[0]->y=5; $array->[1]->x=6; $array->[1]->y=2;
and so forth? If so, how would I be able to sort this with the same rules? Thanks a million PerlMonks!

Replies are listed 'Best First'.
Re: Sorting array with multiple values per keys
by betterworld (Curate) on Aug 12, 2008 at 23:13 UTC
    In addition, is it bad to use the array to put strings with two 'artifical' elements. Is there another way to do it more efficient

    There are definitely more efficient ways, and there are documents like perldsc and perllol that describe them.

    The answer to your first question would probably involve transforming your strings into a proper data structure, sorting them, and transforming them back:

    my @Array; $Array[0] = "1,5"; $Array[3] = "3,9"; $Array[2] = "3,4"; $Array[1] = "6,2"; $Array[4] = "8,1"; @Array = map { join ',', @$_; } sort { $a->[0] <=> $b->[0] or $b->[1] <=> $a->[1] } map { [split /,/] } @Array; use Data::Dumper; print Dumper \@Array;

    (Update:) Now, here's a more efficient / elegant way to do this:

    my @Array = ( [1, 5], [6, 2], [3, 4], [3, 9], [8, 1], ); @Array = sort { $a->[0] <=> $b->[0] or $b->[1] <=> $a->[1] } @Array; use Data::Dumper; print Dumper \@Array;

    You see that the sort block is the same.

    (This uses a list of lists, while kyle's approach below uses a list of hashes, which enables you to use the x and y that you hinted at.)

      No need to recreate the original if you save it.
      @Array = map { $_->[0] } sort { $a->[1] <=> $b->[1] || $b->[2] <=> $a->[2] } map { [ $_, split /,/ ], @Array;
Re: Sorting array with multiple values per keys
by kyle (Abbot) on Aug 12, 2008 at 23:21 UTC
    use Data::Dumper; use List::Util 'shuffle'; # randomized list my @a1 = shuffle ( '1,5', '3,9', '3,4', '6,2', '8,1' ); # convert to an array of hashes my @a2 = map { s/,/,y,/; s/\A/x,/; +{ split /,/ } } @a1; # sort 'x' values ascending and 'y' values descending my @a3 = sort { $a->{x} <=> $b->{x} || $b->{y} <=> $a->{y} } @a2; print Dumper \@a3; __END__ $VAR1 = [ { 'y' => '5', 'x' => 1 }, { 'y' => 9, 'x' => 3 }, { 'y' => 4, 'x' => 3 }, { 'y' => '2', 'x' => 6 }, { 'y' => '1', 'x' => 8 } ];

    To understand the array of hashes, see perlreftut. See the documentation for sort and perlop to understand the <=> stuff in there. Of course, it'd be good to know map, split, and the fact that a list can be turned into a hash and back just by pretending that's what it is. You'd also be doing yourself a favor to learn about Data::Dumper and List::Util if you don't know of them already. They're core modules and awesome.

      my @a2 = map { s/,/,y,/; s/\A/x,/; +{ split /,/ } } @a1;
      is clearer as
      my @a2 = map { /(.*),(.*)/; ( x => $1, y => $2 ) } @a1;
      my @a2 = map { /(.*),(.*)/; { x => $1, y => $2 } } @a1;

      Update: Switched from parens to curlies as per reply.

        And it doesn't change @a1 as a side effect :)

        That is much easier to read, but it actually does something different. I think we'd rather have:

        my @a2 = map { /(.*),(.*)/; { x => $1, y => $2 } } @a1;

        Thanks!

      Thanks for the great reply, guys. I chose to use this as a solution:
      my @Array = ( [1, 5], [6, 2], [3, 4], [3, 9], [8, 1], ); @Array = sort { $a->[0] <=> $b->[0] or $b->[1] <=> $a->[1] } @Array;
      It works great but how do I call the first element of the first element in the @Array now? Something like @Array[0]->[0] ? And can I still use foreach operators now? Like:
      foreach @Elements (@Array) { print @Elements[0]."x".@Elements[1]; }

        Like I already said,
        @Array[0]
        is inappropriate and produces a warning. Use
        $Array[0]->[0]
        or
        $Array[0][0]
        for short.

        foreach $Elements (@Array) { print $Elements->[0] . 'x' . $Elements->[1]; }
        Loop variables in foreach loops must be scalars, but scalars can be array references.
Re: Sorting array with multiple values per keys
by snoopy (Curate) on Aug 12, 2008 at 23:56 UTC
    I know basic sorting but this goes too deep for me. In addition, is it bad to use the array to put strings with two 'artifical' elements. Is there another way to do it more efficient like:
    $array->[0]->x=1;
    $array->[0]->y=5;
    ...
    
    Seems as if you want an array of named values (x, y, ...etc).

    You may want to consider array of hashes (AoH).

    #!/usr/bin/perl use warnings; use strict; use YAML; my @Array; $Array[0]{x} = 1, $Array[0]{y} = 5; # # Or more concisely... # $Array[3] = {x => 3, y => 9}; $Array[2] = {x => 3, y => 4}; $Array[1] = {x => 6, y => 2}; $Array[4] = {x => 8, y => 1}; my @Sorted = sort {$a->{x} <=> $b->{x} || $b->{y} <=> $a->{y}} @Array; print YAML::Dump(@Sorted);
Re: Sorting array with multiple values per keys
by ikegami (Patriarch) on Aug 12, 2008 at 23:31 UTC
    Don't you use warnings?
    >perl -we"my @Array; @Array[0] = '1,5';" Scalar value @Array[0] better written as $Array[0] at -e line 1.

    @Array[LIST] is used when multiple elements are being set or returned.
    $Array[SCALAR] is used to address a single element.