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

Hello Monks, Have a quick question: I am trying to have an array of rows associated with each new IP, so I created a hash based on IP and arrays for each row. I then try:
push (@{%hash->{"IP"}}, @row);
for each row. problem is that when I push then next row X it appends it to the 0 of the array rather than to the X which should be the row number. I am probably missing something simple. Any help would be appreciated.

Replies are listed 'Best First'.
Re: Hashes and Arrays
by Paladin (Vicar) on Jul 16, 2003 at 05:16 UTC
    Assuming I understand what you are saying, that should probably read:
    push @{$hash->{IP}}, [ @row ];
    Which creates a new anonymous array with the contents of @row, and pushes it into the array referenced by $hash->{IP}.
      my @rows; my @newrows; my @printrows; my %newhash; @{%newhash->{$value}}; $rows[0] = "IP"; $newrows[0] = "value1"; $newrows[1] = "value2"; push (@{%newhash->{$rows[0]}}, @newrows); push (@{%newhash->{$rows[0]}}, @newrows); print "\n"; foreach $key (sort keys %newhash) { print "key: $key\n"; @printrows = %newhash->{$key}; print $printrows[0][0]; # should be Value1? print $printrows[1][1]; # should be Value2? print "\n"; }
      tried it with:
      [ @row ]
      and printrows 0,0 just gave me an array. Thanks again.

        First of all, you should probably add a 'use strict;' at the top of your code. It will show you a couple of problems with the code you presented above.

        Second, if you are having problems with data structures, and they are not doing what you want, use Data::Dumper to see what is actually happening. Add the following code snippet after you have built your data structure:

        use Data::Dumper; print Dumper(\%newhash);

        When I ran your code with this, it gave me the following:

        $VAR1 = { 'IP' => [ 'value1', 'value2', 'value1', 'value2' ] };

        So the code is not doing what think it is doing.

        I hope that gives you enough to continue.

        - Cees

        btw, $printrows 0,3 gives the value of what I think should have gone into $printrows 1,1 -- value2. That is what I meant by everything being added to 0 rather than X.
Re: Hashes and Arrays
by sauoq (Abbot) on Jul 16, 2003 at 07:37 UTC

    First things first... %hash->{"IP"} ... don't use that syntax. It's deprecated. It isn't widely used. It is easily confused with a dereference. Did I mention that it's deprecated? It was only by accident that it worked in the first place. It's long and ugly. Oh, and it is deprecated too. Don't use it. Use $hash{IP} instead. You'll be doing yourself a favor.

    Ok, now that that is out of the way, let's rewrite your code a bit. We'll clean up the indenting, combine the declarations and initializations, and remove a little cruft. Let's get rid of your @rows array too; it isn't necessary for this example (all you use it for is to hold the value 'IP'.) We can also get rid of your loop at the end because the example only deals with one key: IP.

    my @newrows = qw( value1 value2 ); my @printrows; my %newhash; push @{$newhash{ IP }}, @newrows; push @{$newhash{ IP }}, @newrows; @printrows = $newhash{ IP }; print $printrows[0][0], "\n"; # should be Value1? print $printrows[1][1], "\n"; # should be Value2? print "\n";
    There. Now your question becomes a little more clear. It seems you are a little confused about how complex data structures work.

    Step by step...

    push @{$newhash{ IP }}, @newrows;
    After that line is evaluated, $newhash{ IP } holds a reference to an array. The elements of the array are copies of the same two elements that are in @newrows, namely 'value1' and 'value2'.

    After you do it again with

    push @{$newhash{ IP }}, @newrows;
    the array that your reference, $newhash{ IP }, refers to contains 2 copies of the @newrows array. It might be easier to see what you are doing if you remove some complexity. Examine the one-liner perl -le 'my @a = (1, 2); push @b, @a; push @b, @a; print for @b' and its output.

    So, that's the mistake you made during construction of your data structure. Now let's take a closer look at how you attempt to use it.

    @printrows = $newhash{ IP };
    That doesn't do what you expect it to. After that, @printrows has one element: the reference stored in $newhash{ IP }. If you want to copy the array refered to by that reference, then you'd need something like @printrows = @{ $newhash{ IP } }; instead.

    Moving on...

    print $printrows[0][0], "\n"; # should be Value1?
    Indeed, that is 'value1'. Just not for the reasons that you'd like it to be. That only works because of the error you made in the last step. Remember that @printrows holds a single value, a reference. That reference, $printrows[0] is a reference to the array you constructed. The first element in that array, $printrows[0]->[0] which can also be written in short form as $printrows[0][0], is 'value1'.

    The next line

    print $printrows[1][1], "\n"; # should be Value2?
    fails to print 'value2'. Once again, recall that @printrows only has one element. That element is indexed by 0 (where we found it above.) There is no element at index 1, so $printrows[1] evaluates to undef. So does $printrows[1][1] for that matter. However, had you been running with warnings enabled (either with the -w switch to perl or the use warnings pragma), you would have found that this line resulted in the message "use of uninitialized value in print at ..." which might have helped some.

    You should probably read some of the tutorials that come with perl like perlreftut, perldsc, perllol. For reference, you should also familiarize yourself with perldata.

    Good luck!

    -sauoq
    "My two cents aren't worth a dime.";
    
      Thanks for all your help and links. I fixed up my perl coding a bit now. However, I still have the core problem. To iterate through each row, I have to do something like this:
      my $x = 0; while ($printrows[0][$x]) { print "First: $rows[0][$x]\n"; $x++; print "Second: $rows[0][$x]\n"; $x++; }
      I really want to do this for each row:
      my $x = 0; while ($printrows[$x][0]) { print "First: $rows[$x][0]\n"; print "Second: $rows[$x][1]\n"; }
      Because, then I can reference a row directly without having to do: $x=2 X 15, $rows[0]$x to get to the 15th row.

        You should probably forget about using a separate @printrows variable as you are initializing it to contain only one value anyway. Why not just use the original $newhash{ IP }?

        That's supposed to be an array reference anyway.

        I think I might have a handle on what you are trying to do now...

        my %newhash; push @{ $newhash{IP} }, [ 'row 0, col 0', 'row 0, col 1' ]; push @{ $newhash{IP} }, [ 'row 1, col 0', 'row 1, col 1' ]; # $newhash{ IP } contains a reference to an array with two elements. # Each element is itself a reference to an array with two elements. print $newhash{IP}->[0][0], "\n"; # row 0, col 0 print $newhash{IP}->[0][1], "\n"; # row 0, col 1 print $newhash{IP}->[1][0], "\n"; # row 1, col 0 print $newhash{IP}->[1][1], "\n"; # row 1, col 1

        -sauoq
        "My two cents aren't worth a dime.";