Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

dynamic 2d array

by Anonymous Monk
on Aug 13, 2008 at 23:53 UTC ( [id://704254]=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monks,
I'm stuck on a problem and I hope that one of you may be able to help.
Initially, I thought the problem was that an variable in the main program was not accessible to a subroutine. After much testing, I'm now fairly certain the problem lies with how I'm trying to define the array. I've cut the program down to the smallest I can to demonstrate my issue.
What is the best way to define a 2d array without knowing exactly how big the array will be? Any help appreciated.

!/usr/bin/perl -Tw #use T taint, and W warnings use strict; use Data::Dumper; require Text::CSV; #---------------------------------------------------------- sub printRow{ #input the row number print Dumper(@d_array); } #---------------------------------------------------------- #read the csv my $csv_filename="test.csv"; # Getting input into working file my $line; my $line_number; my $csv = Text::CSV->new; my $separator = ','; my $column = ''; my @d_array; my $cnt; unless (open(INFILE, $csv_filename)){ print "Cannot open file \"$csv_filename\"\n"; exit; } foreach $line (<INFILE>){ $line_number++; if ($csv->parse($line)) { my @field = $csv->fields; my $count = 0; for $column (@field) { ++$count; $d_array[$line_number][$count]=$column; } } else { my $err = $csv->error_input; print "parse() failed on argument: ", $err, "\n"; } close INFILE } print Dumper(@d_array); &printRow($cnt);

Replies are listed 'Best First'.
Re: dynamic 2d array
by GrandFather (Saint) on Aug 14, 2008 at 02:02 UTC

    Your immediate problem has been resolved. However there are a few other things you ought to consider:

    unless (open(INFILE, $csv_filename)){ print "Cannot open file \"$csv_filename\"\n"; exit; }

    is better written:

    open my $inFile, '<', $csv_filename or die qq{Cannot open file "$csv_f +ilename"\n};

    which declares a lexical file handle, uses the safer three parameter version of open and uses the more idiomatic die for error handling.

    foreach $line (<INFILE>) {

    slurps the file and introduces a variable that, in a subtle fashion, isn't the one you think it is (see Foreach Loops and note that the variable is aliased). You would be better to use a while loop:

    while (defined (my $line = <$inFile>)) {

    The close INFILE should be outside the loop. It didn't matter for the foreach loop because you were slurping the file, but it will cause trouble for the while loop version.

    my @field = $csv->fields; my $count = 0; for my $column (@field) { ++$count; $d_array[$line_number][$count] = $column; }

    is better written:

    $d_array[$line_number] = [$csv->fields ()];

    Update: oh, and if you use $. instead of $line_number you get the actual file line number. Note though that $d_array[0] at least will be undef using either your current line counting technique or the $. technique.


    Perl reduces RSI - it saves typing
Re: dynamic 2d array
by jethro (Monsignor) on Aug 14, 2008 at 00:28 UTC

    my variables are defined strictly lexical. That means d_array is only defined and known after line 23, but not in line 10 where you already use it. Move the line 'my d_array;' before the subroutine definition and it should work. Even better would be to give the array to the subroutine as a parameter:

    sub printRow{ #input the row number print Dumper(@_); } # and later: &printRow(@d_array);

    By the way, your for-loop can be replaced by this simple line:

    $d_array[$line_number]= [@field];
      Thanks all.
      Moving the definition worked.
      The textbook I have says it doesn't matter where you define a variable. Time to get a better text book!
      Thanks again

        Depends on how you define it. The text is talking about global variables, which are available to code outside their containing block:

        { $i = 3; } print "$i\n";

        Prints 3. Compare with:

        { my $i = 3; } print "$i\n";

        Without strict, this prints only a newline. This code declares a lexically scoped $i, which only persists through the end of its enclosing block. One of the best things about strict/warnings is that perl will complain about this kind of thing, possibly saving you a bunch of time hunting down a confusing error in a large program.

Re: dynamic 2d array
by NetWallah (Canon) on Aug 14, 2008 at 01:10 UTC
    You could avoid several temporary variables (line_number, count, column, @field) by doing:
    push @d_array, [ $csv->fields() ];
    If you wanted to get fancy (and cryptic), you could try:
    @d_array = map { $csv->parse($_) ? [ $csv->fields() ] : warn($csv->error_input), () } <INFILE>;

    I also suggest you pass a reference to printrow():

    sub printRow{ my ($aref) = @_; print Dumper($aref); } ## call like this .. printRow(\@d_array); # using & is deprecated
    UpdateFix typo, and formatted better.

         Have you been high today? I see the nuns are gay! My brother yelled to me...I love you inside Ed - Benny Lava, by Buffalax

Re: dynamic 2d array
by dreadpiratepeter (Priest) on Aug 14, 2008 at 00:05 UTC
    You never actually stated what the problem is or what the error you are getting is, but if it is what I think (without running the code() is is, try adding:
    $d_array[$line_number]=[];
    right after the $line_number++ line. That will properly initialize the inner array.
    you will still have a problem though, you need to either initialize $line_number to -1 before the loop, or move the $line_number++ to the end to avoid having your loop start at 1;


    -pete
    "Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere."
      Sorry, my bad. Been up all night trying to fix this thing and getting a little groggy. The error is this:

      Global symbol "@d_array" requires explicit package name at ./subtest.p +l line 10. Execution of ./subtest.pl aborted due to compilation errors.
        As you have it, the scope of the my @d_array does not include your sub printRow. Move the sub declaration to the end, or move the my declaration to the top.

        Also, you probably want to be passing array refereces to Dumper (or at least be aware of the different output in the two cases):

        use strict; use warnings; use Data::Dumper; my @ar = qw( a b c ); print Dumper(\@ar); print Dumper(@ar); __END__ $VAR1 = [ 'a', 'b', 'c' ]; $VAR1 = 'a'; $VAR2 = 'b'; $VAR3 = 'c';
Re: dynamic 2d array
by Tux (Canon) on Aug 18, 2008 at 17:15 UTC

    Chiming in a bit late after a YAPC::EU visit.

    Note that parsing CSV like this is not perfectly safe. Using $csv->getline () is not only much safer, but also solves your question instantly:

    open my $fh, "<", $csv_filename or die "Cannot open file \"$csv_filena +me\": $!\n"; while (my $row = $csv->getline ($fh)) { $d_array[++$line_number] = $row; # or even nicer # push @d_array, $row; } $csv->eof or $csv->error_diag (); close $fh or die "Cannot open file \"$csv_filename\": $!\n";

    See how elegant and short?


    Enjoy, Have FUN! H.Merijn

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://704254]
Approved by Joost
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2024-04-19 04:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found