5

I am new to Perl and stuck with a (likely simple) array sorting problem.

I've inherited some Perl code that reads lines from a text file into three 1-D arrays (x,y,z). I'd like to be able sort these arrays using one of the dimensions as the key and reordering the other two dimensions to match.

For example, if my input is:

  • @x = (1, 3, 2)
  • @y = (11,13,12)
  • @z = (21,23,22)

and I sort by x, I'd like the result to be:

  • @x = (1, 2, 3)
  • @y = (11,12,13)
  • @z = (21,22,23)

I could merge the three 1-D arrays into a 2-D array if that makes life easier.

1
  • your example data could be cleared - the values in @y and @z are in the same order as @x. having very different values (or even something like 'a','b','c') would make it clearer exactly how they are to be sorted Commented Jan 5, 2011 at 17:57

6 Answers 6

10

Merging all the arrays together isn't necessary. Use sort to get the correct index ordering for the elements in @x:

@sort_by_x = sort { $x[$a] <=> $x[$b] } 0 .. $#x;    #   ==> (0, 2, 1)

Then apply that index ordering to any other array:

@x = @x[@sort_by_x];
@y = @y[@sort_by_x];
@z = @z[@sort_by_x];
Sign up to request clarification or add additional context in comments.

Comments

3
use strict;
use warnings;
use Data::Dumper;

use List::Util qw(reduce);

my @x = (1, 3, 2);
my @y = (11, 13, 12);
my @z = (21, 23, 22);

my @combined = map { [ $x[$_], $y[$_], $z[$_] ] } 0 .. $#x;
my @sorted = sort { $a->[0] <=> $b->[0] } @combined;
my $split_ref = reduce { push @{$a->[$_]}, $b->[$_] for 0 .. $#$a; $a;} [[], [], []], @sorted;

print Dumper \@combined;
print Dumper \@sorted;
print Dumper $split_ref;

Which will essentially give you:

    [
      [
        1,
        2,
        3
      ],
      [
        11,
        12,
        13
      ],
      [
        21,
        22,
        23
      ]
    ];

Comments

0

Merging indeed may make life easier.

@sorted = sort { $a->[0] <=> $b->[0] } 
    ( [$x[0], $y[0], $z[0]], [$x[1], $y[1], $z[1]], [$x[2], $y[2], $z[2]] );

might do it. You'll of course not want to write all that out by hand!

Comments

0

If @x is the same size as @y and @z, there is no need to sort - You can use an array slice instead!

use strict;
use warnings;
use 5.010;

my @x = (1, 3, 2);
my @y = (11,13,12);
my @z = (21,23,22);

say join ', ', @y;
@y = @y[ map { $_ - 1 } @x ]; #We use map create a lists of the values of elements in @x, minus 1.
say join ', ', @y;

Of course, if you just want to sort by numerical order, then using @x is superfluous:

@y = sort { $a <=> $b } @y;

Finally, if you want to sort an arbitrary number of arrays, you could create an array of arrays, or pass a list of references to a for, ala

my @indexes = map { $_ - 1 } @x;

for my $array_ref ( \@x, \@y, \@z ) {
    @$array_ref = @{$array_ref}[@indexes];
}

4 Comments

I don't think you can safely assume that values in @x can be used as indices. What if they're negative or not unique?
Negative numbers or repeats wouldn't really be troublesome (it's his spec, after all..), but an index outside of the array will trigger warnings. But yeah, caveat programmer.
They are troublesome in that they will give you incorrect results without a warning. Try changing the unsorted @x to (1,3,3).
Again, his spec - From the OP: "and i sort by x" I took this to mean that he isn't simply sorting the arrays numerically.
0

Have a try with:

#!/usr/bin/perl 
use 5.10.1;
use strict;
use warnings;
use Data::Dumper;

my @x = (1, 3, 2);
my @y = (11,13,12);
my @z = (21,23,22);

my (%y, %z);

@y{@x} = @y;
@z{@x} = @z;

my @xs = sort @x;
my @ys = @y{@xs};
my @zs = @z{@xs};
say Dumper \@xs,\@ys,\@zs;

Output:

$VAR1 = [
          1,
          2,
          3
        ];
$VAR2 = [
          11,
          12,
          13
        ];
$VAR3 = [
          21,
          22,
          23
        ];

1 Comment

This breaks if @x has any duplicate values.
-1

Do

@a = sort { $a <=> $b } @a;

Also see sort on perldoc.

2 Comments

That won't sort @y or @z.
yes, that will sort one of the 1-d arrays. it won't sort the other two to match. my hope is to be able to sort by the x array and have the y and z array reordered to match so that the lines from the text file are preserved. i'm sorry if my question wasn't clear. --amb

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.