Reputation: 335
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:
and I sort by x, I'd like the result to be:
I could merge the three 1-D arrays into a 2-D array if that makes life easier.
Upvotes: 5
Views: 2979
Reputation: 118595
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];
Upvotes: 10
Reputation: 91373
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
];
Upvotes: 0
Reputation: 9689
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
]
];
Upvotes: 3
Reputation: 1259
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];
}
Upvotes: 0
Reputation: 13832
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!
Upvotes: 0