Transpose in perl
I have started learnin开发者_JAVA技巧g perl and like to try out new things.
I have some problem in text processing. I have some text of the form,
0 1 2 3 4 5 6 7 8 9 10
6 7 3 6 9 3 1 5 2 4 6
I want to transpose this text. Like, I want to make rows as columns ans columns as rows. Id there a way to do this in perl?
Thank you all.
So this solution uses an array-of-arrays, each nested array is a row of data. Very simply you loop over the columns in each row and push them onto another array-of-arrays using the column index as the index which to push the value onto. This has the effect of pivoting the data as you requested.
#!/usr/bin/env perl
my @rows = ();
my @transposed = ();
# This is each row in your table
push(@rows, [qw(0 1 2 3 4 5 6 7 8 9 10)]);
push(@rows, [qw(6 7 3 6 9 3 1 5 2 4 6)]);
for my $row (@rows) {
for my $column (0 .. $#{$row}) {
push(@{$transposed[$column]}, $row->[$column]);
}
}
for my $new_row (@transposed) {
for my $new_col (@{$new_row}) {
print $new_col, " ";
}
print "\n";
}
This results in:
0 6
1 7
2 3
3 6
4 9
5 3
6 1
7 5
8 2
9 4
10 6
Here's an outline of one way to transpose data. Working through this example will be instructive because you will need to use CPAN, you will learn about the useful List::Util
and List::MoreUtils
modules, you will learn the basics of complex data structures (see perlreftut, perldsc, and perllol), and you will get to use an iterator in Perl.
use strict;
use warnings;
use List::MoreUtils qw(each_arrayref);
my @raw_data = (
'0 1 2 3 4 5 6 7 8 9 10',
'6 7 3 6 9 3 1 5 2 4 6',
);
my @rows = ... ; # Look up map() and split() to fill in the rest.
# You want an array of arrays.
my @transposed; # You will build this in the loop below.
my $iter = each_arrayref(@rows); # See List::MoreUtils documentation.
while ( my @tuple = $iter->() ){
# Do stuff here to build up @transposed, which
# will also be an array of arrays.
}
There certainly is, and Mike has pointed out the simplest way. If you are learning, you probably want to write your own function?
First, you want to split each line on spaces to get an array of values (or push the list of words into the array, as in Dalton's answer; in Perl, there's always more than one way to do anything)
Then, for each element in the array, you want to print it and its counterpart in the second array onto the same line. (What will you do if one array runs out before the other?)
Of course, if you want to learn Perl, you will also definitely want to learn to use CPAN as well, so it's still worthwhile to try using Data::Pivot.
use strict;
# read the first line
my @labels = split ' ', <>;
# read and ignore the empty second line
<>;
# read the third line
my @values = split ' ', <>;
# transpose (I suppose you'll do more with the table than just printing it)
my %table = map { $labels[$_] => $values[$_] } 0..$#labels;
# print
foreach (@labels) {
print "$_ $table{$_}\n";
}
Here's my new script to transpose a tab-delimited file. Change \t to your delimiter if you like.
#!/usr/bin/perl -anF/\t|\n/
$n = @F - 1 if !$n;
for $i (0..$n) {
push @{ $m->[$i] }, $F[$i];
}
END {
for $r (@$m) {
print join("\t", @$r), "\n";
}
}
or as a 104 character "one liner" (with apostrophe-backslash-newline-apostrophe added to avoid horizontal scrolling):
perl -anF'\t|\n' -e'$n=@F-1if!$n;for(0..$n){push@{$$m[$_]},$F[$_]}'\
'END{print map{join"\t",@$_,"\n"}@$m}'
use List::UtilsBy qw/zip_by/;
my @transposition = zip_by { [ @_ ] } @matrix;
https://metacpan.org/pod/List::UtilsBy#zip_by
I ran across this question while searching for a way to transpose an AoA I built by reading a file, but of course, this solution will still work for any AoA, and it doesn't require the array references have equal numbers of elements. I know this is an old question, but as I'm still learning, it would be awesome to have some critique/examples of how to improve my solution.
my $max_rows = 0;
foreach my $line (@copy) {
my $ref = ();
push @$ref, split(/\s+/, $line);
my $rows = scalar(@$ref);
$aoa[$i] = \@$ref;
$i++;
if ($rows > $max_rows) {
$max_rows = $rows;
}
}
for (my $i = 0; $i <= $max_rows; $i++) {
my @aoa_t = ();
for (my $k = 0; $k <= $#aoa; $k++) {
if ($aoa[$k]->[$i]) {
push @aoa_t, $aoa[$k]->[$i];
} else {
push @aoa_t, " ";
}
print " @aoa_t\n";
}
if ($aoa_t[0] eq " ") {
last;
} else {
$t_aoa[$i] = \@aoa_t;
}
}
It worked very well for a file with 200+ lines of data in space delimited format. Any feedback is appreciated, if that is appropriate in this forum. Regards.
how about Array-Transpose-0.06 ?
http://metacpan.org/pod/Array::Transpose
use strict;
my ($i, $rows, $cols) = (0, 10, 100);
# initiate array 10x100
my $array = [map {[map {$i++} (1..$cols)]} (1..$rows)];
# transpose array into 100x10 array
my $transpose = [map {[map {shift @$_} @$array]} @{$array->[0]}];
array must be a matrix, i.e columns must be equal for each row, original array will be destroyed
this code will not use extra memory for transpose, x2 for other libraries, for large array for example 100x1M, it matters
精彩评论