Perl two dimensional array handling

Most of the programming languages provide multi dimensional array. But in Perl you can't declare this types of array directly. If you want to declare a 2d array in Perl, you have to declare first few single dimensional array. Check the Examples below.

## Declare few single dimensional array
my @row1 = qw(71 22 15 10 51);
my @row2 = qw(91 82 28 11 91);
my @row3 = qw(11 72 37 58 20);
my @row4 = qw(21 42 63 24 16);
my @row5 = qw(81 32 53 54 42);

Then you have to declare another array that takes the references of the arrays you declared before. This combination will make a 2d array

## Adding all the single dimensional array reference into another array
my @array_2d = (\@row1, \@row2, \@row3, \@row4, \@row5);

Assuming your 2d array is a square matrix, Now print your array using index(i mean $i,$j that you done at most of the programming languages like C/C++)

print "Print Using Array Index\n";
for(my $i = 0; $i <= $#array_2d; $i++){
   # $#array_2d gives the highest index from the array
   for(my $j = 0; $j <= $#array_2d ; $j++){
      print "$array_2d[$i][$j] ";
   }
   print "\n";
}

Not always that you'll know the number of length of your array. You can use foreach keyword of Perl to traverse your array easily. At below code, First it is traversing the row, then it traverse the column of each row, and print the value from array.

print "Print Using ForEach\n";
foreach my $row(@array_2d){
   foreach my $val(@$row){
      print "$val ";
   }
   print "\n";
}

If you want to sort each row of your 2d array, you can use the sort function of Perl. Just pass the array into the function, and it will return the sorted array.

print "Sort Each Row\n";
foreach my $row(@array_2d){
   foreach my $val(sort(@$row)){
      print "$val ";
   }
   print "\n";
}

You can also reverse each row elements same as sort. You have to use reverse function, that is built in with Perl.

print "Reverse Each Row\n";
foreach my $row(@array_2d){
   foreach my $val(reverse(@$row)){
      print "$val ";
   }
   print "\n";
}

Finally you can combine the sort and reverse function. First sort the array. Then reverse it. Or you can do first reverse, then sort.

print "Sort Then Reverse Each Row\n";
foreach my $row(@array_2d){
   foreach my $val(reverse(sort(@$row))){
      print "$val ";
   }
   print "\n";
}

Hope you have enjoyed the fun of Perl multi dimensional array handling with few built in function like foreach, sort, reverse etc.

Comments

Anonymous said…
The unfortunate part of creating a 2D array with references is that the 2D array does not have its own memory space. Any changes to the original references effects the contents in the 2D array strucutre. In addition, if the 2D array was going to be an unknown length you can't create an unlimited number of 1D arrays to handle this situation. Is there another way handle it so you can deal with these issues?

thanks,
Cliff
James said…
Hi cliff,

My experience of perl at the moment is limited, although I have been learning quite fast and have already written a number of scripts to aid my data analysis. (I'm a physics researcher.)

The second question is easy to answer - you do not need predefine the size of the 2D array. If you want to add a new column or row you simply do it. One of my scripts reads in an output file generated by a different code and is then displayed using gnuplot. (I need to do this because the format of the initial file is hideous.) The file contains n sets of energies, each enegery containing x number of values. n and x can (and in almost all cases are) different from file to file and are different from each other; the data does not form a square matrix. I have written a script that simply creates new columns and rows on the fly until all of the data has been read in, parsed, written to an output file, graph plotted, displayed and saved to file using gnuplot.

As for your first question, I have to confess that I'm not entirely certain I know what you mean. If you change the reference then yes, the values will change. Equally, if a (the) value(s) change in the referenced array then the corresponding 2D array element(s) will change accordingly. Either way, you have to tell it to do that; references and values do not change spontaneously and only change if these changes are hard coded or as the result of user interaction.
Anonymous said…
Hi Cliff,

Hopefully you've sorted out your question by now, but if anyone else is reading this page and wondering the same thing, I've figured out a solution. I'm new to perl, but I did some random messing around and came up with this solution.

The problem is if we define 2 arrays @a=(1,2,3) and @b=(7,8,9), and then define @c=(\@a, \@b), then @c is just storing pointers to @a and @b. This means if we modify @a or @b, for example let @b=('cat','dog','rabbit'), this will modify the contents of @c.

If you want to copy the contents of @a and @b into @c instead, you can use @c=([@a], [@b]). Now we can change @a or @b, and @c will still store the original contents of @a and @b.

Hope someone finds this helpful.

-R
Unknown said…
Perl DOES allow 2d array creation withour reference. But no ready made data structure for it.This is easy even when your rows and columns counts are not known i.e rows X Columns matrix. Lets say u have a comma separated file having a data table which u want to load as a 2d matrix.

open CSVFILE,$file or die;#open input file
my @matrix; #declare 2d array
my $i=0; #counter
while()
{
chomp $_;
my @row=split ",",$_;
for ($j=0; $j<@rows;$j++)
{
$matrix[$i][$j]=$row[$j]; #fill 2d matrix with data
}
$i++;
}

So, each cell of the 2d matrix now holds the actual data instead of reference to it. For sorting:--
@matrix_sorted=sort{$b->[0]<=>$a->[0]}@matrix;

This will numerically sort it descending on column 1 values. But Alas, u cannot use multisort here like PHP does. Hoping someone would offer a solution somewhere.
James said…
I hate to contradict you Amit, but Perl *does NOT* handle 2D arrays like most people think of them. The Perl developers say so themselves! You only ever have three types of data in Perl:

@list - a *list* of scalar variables
$scalar - a single scalar variable
%hash - a list of scalar variables where entries are labelled not by row number but by a label of your choosing.

The way you create 2D arrays in Perl is to make a list of lists, that is, suppose you have, @matrix[$i][$j], the rows labelled $i contain pointers to other lists. You can think of this by considering a column of entries, and coming out perpendicular to these entries a separate list of values.

Lets populate @matrix:

@matrix = ([0, 1, 2], [3, 4, 5]).

What we have done here is generate two anonymous arrays containing [0,1,2] and [3,4,5] and one named array, @matrix. If you wanted to print out the first row you cannot go

print "$matrix[0]"

as that will print out the memory pointer and Perl never dereferences things for you.

Even if you go:

$matrix[0][0] = 0;
$matrix[0][1] = 1;
$matrix[0][2] = 2;
$matrix[1][0] = 3;
$matrix[1][1] = 4;
$matrix[1][2] = 5;

you STILL have an anonymous arrays, the locations of which are stored as a pointers in $matrix[0] and $matrix[1].

You can test this for yourself using this piece of code:

my @matrix = ([1,2,3],[4,5,6]);
print "@matrix\n";
print "$matrix[0]\n";
print "$matrix[0]->[1]\n";
my $aref=$matrix[1];
print "[@{$aref}]\n";
Anonymous said…
for(my $j = 0; $j <= $#array_2d ; $j++){

here j shouldn't be compared to the size of array_2d but to the size of the i'th array in array_3d