http://www.perlmonks.org?node_id=1039251

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

UPDATE I'll state the problem I'm trying to solve, which I didn't do originally. I thought the added detail would just clutter the post, but since I'm still stuck here goes.

I have a large array of arrays, the original data. When going through that data, serially, at each row I need to check back over the previous 15 rows to see if a value exists in a specified field of those previous rows. Call it a look-back. The value I'm looking for can change based on the values in the current row, so this has to be evaulated with every original data row.

I decided to populate a look-back array with rows from the original data. This would be the 15 previous rows. The flow concept goes like this:

run through the original data via foreach. For every original data row :
while there are less than 15 rows in the look-back array, push each original data row onto the look-back array
after there are 15 rows in the look-back array, scan the look-back array for the specified value,
then shift the first row off of the look-back array and push the current original data row onto the look-back array
repeat until the end of the original data

The look-back array would thus always have the previous 15 rows, based on the current original data row. Is this flow concept so far off that it can't be done ?

I had considered setting up a while loop with a counter to go through the original data, and using that counter to look back over the last 15 rows. That seems awful clumsy to me, but maybe in this case ! TMTOWTDI...

Original Post

I've got an array of arrays from which I can't access single elements. Seems to me I've used this syntax before, but apparently my neurons are misfiring because it's not working the way I think it should.

When trying to retrieve one element of the array of arrays using the form

$OneElement = $Records[ 1 ][ 2 ];

I get an error that I can't use a string as an Array ref with strict, and it lists the entire row from the array as the string. It appears that Perl is only using the first index, and returning the entire row. I'm sure this is what I'm telling Perl to do, I just can't see how I'm telling it to do that.

Here's a demo code which reproduces the error message. I've commented out the $OneElement assignment so the rest runs. With the last two statements, commenting out the first one results in the same error from the second print statement - only the line number is different.

#!C:\Perl\bin use diagnostics; use strict; splice( my @Records ); splice( my @RecordRowFields ); splice( my @RecCheck ); my $RecordRow = ""; my $OneElement = ""; push( @Records, join ',', ( "AAA", 20130610, 730, 1015, "\n" ) ); push( @Records, join ',', ( "BBB", 20130610, 1015, 1200, "\n" ) ); push( @Records, join ',', ( "CCC", 20130610, 1230, 1400, "\n" ) ); push( @Records, join ',', ( "DDD", 20130610, 1415, 1530, "\n" ) ); #$OneElement = $Records[ 1 ][ 2 ]; foreach $RecordRow( @Records ) { @RecordRowFields = split /,/, $RecordRow; push( @RecCheck, join ',', @RecordRowFields ); } print "Array Records\n"; print @Records; print "\n\n"; print "Array RecCheck\n"; print @RecCheck; print "\n\n"; print "$Records[ 1 ][ 3 ]\n"; print "$RecCheck[ 1 ][ 3 ]\n";
Anyone seen this (newbie mistake) before ?

Dyslexics Untie !!!

Replies are listed 'Best First'.
Re: element of an array of arrays
by frozenwithjoy (Priest) on Jun 17, 2013 at 02:14 UTC

    You don't actually have an array of arrays:

    use Data::Printer; p @Records; p @RecCheck;
    [ [0] "AAA,20130610,730,1015, ", [1] "BBB,20130610,1015,1200, ", [2] "CCC,20130610,1230,1400, ", [3] "DDD,20130610,1415,1530, " ] [ [0] "AAA,20130610,730,1015, ", [1] "BBB,20130610,1015,1200, ", [2] "CCC,20130610,1230,1400, ", [3] "DDD,20130610,1415,1530, " ]

    I left your @Records almost as is (only removed new line) and changed @RecCheck so that it would be an array of arrays like I think you wanted:

    #!/usr/bin/env perl use strict; use warnings; use feature 'say'; use Data::Printer; my @Records; push( @Records, join ',', ( "AAA", 20130610, 730, 1015 ) ); push( @Records, join ',', ( "BBB", 20130610, 1015, 1200 ) ); push( @Records, join ',', ( "CCC", 20130610, 1230, 1400 ) ); push( @Records, join ',', ( "DDD", 20130610, 1415, 1530 ) ); my @RecordRowFields; my @RecCheck; for my $RecordRow (@Records) { @RecordRowFields = split /,/, $RecordRow; push( @RecCheck, [@RecordRowFields] ); } say "Single element: $RecCheck[1][2]"; p @RecCheck; __END__ [ [0] [ [0] "AAA", [1] 20130610, [2] 730, [3] 1015 ], [1] [ [0] "BBB", [1] 20130610, [2] 1015, [3] 1200 ], [2] [ [0] "CCC", [1] 20130610, [2] 1230, [3] 1400 ], [3] [ [0] "DDD", [1] 20130610, [2] 1415, [3] 1530 ] ] Single element: 1015
      So when I push arrays onto an empty array, that doesn't create an array of arrays ?

      Interesting, that's not where I thought the problem was. Now I'm at a loss. How would I create an array of arrays with that data ?

      Dyslexics Untie !!!
        I just updated my answer w/ code that creates an AoA. You were basically pushing a list into an array and it gets incorporated into the array just like all the elements. What you needed to do was push an array reference.

        So when I push arrays onto an empty array, that doesn't create an array of arrays ?

        an array of arrays is an array of arrayrefs

        When you push @arra, @array you're pushing a list onto @arra

        push @arra, \@array; ## pushing arrayreference, array of arrays achieved

Re: element of an array of arrays
by kcott (Archbishop) on Jun 17, 2013 at 05:19 UTC

    G'day JockoHelios,

    From your original post and your replies to frozenwithjoy and LanX, you appear to be experiencing a fair amount of confusion. Perhaps this short annotated script might inject some clarity:

    $ perl -Mstrict -Mwarnings -le ' use Data::Dumper; # Declare an empty array (Note: no "splice" required) my @records; # Initialise the array with 2 elements # Each element must be a scalar # If an element is an array of values, you need an arrayref @records = ( [ "A", 1, 2, 3 ], [ "B", 4, 5, 6 ] ); print "\$records[1][2]: ", $records[1][2]; # Push another array of values (as an arrayref) push @records, [ "C", 7, 8, 9 ]; print "\$records[2][2]: ", $records[2][2]; # "join" creates a string (strings are scalar values) push @records, join ",", "D", 10, 11, 12; print "\$records[3]: ", $records[3]; # Add a hash (as a hashref - which is also a scalar value) push @records, { E => 13, F => 14, G => 15 }; print "\$records[4]{G}: ", $records[4]{G}; # The entire data structure: print Dumper \@records; ' $records[1][2]: 5 $records[2][2]: 8 $records[3]: D,10,11,12 $records[4]{G}: 15 $VAR1 = [ [ 'A', 1, 2, 3 ], [ 'B', 4, 5, 6 ], [ 'C', 7, 8, 9 ], 'D,10,11,12', { 'G' => 15, 'F' => 14, 'E' => 13 } ];

    See also: splice, join, perldsc - Perl Data Structures Cookbook.

    -- Ken

Re: element of an array of arrays
by LanX (Saint) on Jun 17, 2013 at 02:21 UTC
    this pushes one joined line

    DB<103> push( @Records, join ',', ( "AAA", 20130610, 730, 1015, "\n" + ) ); => 1 DB<104> \@Records => ["AAA,20130610,730,1015,\n"]

    you rather want something like

    DB<106> push @Records, [ "AAA", 20130610, 730, 1015 ]; => 1 DB<107> \@Records => [["AAA", 20130610, 730, 1015]] DB<108> $Records[0][2] => 730

    see perldsc#ARRAYS OF ARRAYS

    Cheers Rolf

    ( addicted to the Perl Programming Language)

    UPDATES

    extended code examples

      The joins were an attempt to correct the problem I was initially running into.

      Without the joins, that syntax produced a similar error. But it was stating that I couldn't use the string "20130610" as an array reference. Using the joins produced the error with the entire row as the string I couldn't use.

      Dyslexics Untie !!!
        Sorry I don't understand you!

        Could you plz give me an code example reproducing this other error?

        Maybe the problem is connected to the fact that you are trying to print the AoA in a whole

        print  @array

        ... this will result in a list of stringified array-refs.

        ARRAY(0x8bfc3f8) ...

        Better use Data::Dumper Dumper like kcott showed you or print the single subarrays to get the leaf-elements stringified.

        print "@$_\n" for @array;

        HTH

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: element of an array of arrays
by hdb (Monsignor) on Jun 17, 2013 at 09:18 UTC

    I am a bit confused about all this joining and splitting. All you need to do is:

    use strict; use warnings; my @Records; push( @Records, [ "AAA", 20130610, 730, 1015, "\n" ] ); push( @Records, [ "BBB", 20130610, 1015, 1200, "\n" ] ); push( @Records, [ "CCC", 20130610, 1230, 1400, "\n" ] ); push( @Records, [ "DDD", 20130610, 1415, 1530, "\n" ] ); print $Records[1][2], "\n";

      And with greater terseness and, I would argue, much greater readability and maintainability:

      >perl -le "use strict; use warnings; use Data::Dump; ;; my @Records = ( [ 'AAA', 20130610, 730, 1015, qq{\n} ], [ 'BBB', 20130610, 'Hello', 1200, qq{\n} ], [ 'CCC', 20130610, 1230, 'There!', qq{\n} ], [ 'DDD', 20130610, 1415, 1530, qq{\n} ], ); ;; print $Records[1][2], qq{ $Records[2][3]}; ;; dd \@Records; " Hello There! [ ["AAA", 20130610, 730, 1015, "\n"], ["BBB", 20130610, "Hello", 1200, "\n"], ["CCC", 20130610, 1230, "There!", "\n"], ["DDD", 20130610, 1415, 1530, "\n"], ]
Re: element of an array of arrays
by AnomalousMonk (Archbishop) on Jun 17, 2013 at 14:36 UTC
    splice( my @Records );

    I think you have used this personal idiom before, and I even seem to remember a discussion about it in some other thread.

    This is very puzzling to me. Can you say or provide a link back to a previous statement of why you prefer this particular idiom?

    I know that some authorities advocate the occasional use of a statement like
        my $scalar = undef;
    or
        my @array = ();
    as a way of obviating the need for a comment to the effect
        # this thing really needs to be (undefined|empty) for when it is used later in the script
    (of course, by default scalars are undefined and arrays empty upon creation). However, the use of splice in this context, if that is the intent, seems likely to produce only a blank stare in a future reader/maintainer (who may even be yourself!), followed by a scramble to the docs to find the effect on an array of  splice when used with no other parameters.

    Yours in confoositude?

      AnomalousMonk++

      But since it has no effect to empty a freshly declared array in such a complicated way, I'd rather classify it as cargo cult (sorry) nonsense or even try-and-error coding. =)

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        I'd rather classify it as cargo cult nonsense.

        And I'd be so inclined myself except that I can't remember ever seeing this particular usage in any other code from which JockoHelios might have cargo cult-ed it into his or her own; it seems to be an utterly unique creation.

      Earlier on (a couple of weeks, at least) after starting to use strict, I'd tried to declare arrays and had gotten error messages. To get past that, I'd started declaring arrays using splice. It always struck me as odd, but since it worked I continued to do it that way.

      I didn't think of the simple declaration syntax that hdb mentioned above. I thought I had to actually do something with an array when declaring it. That's the reason I've been declaring arrays using splice, which of course I can discontinue :)

      Dyslexics Untie !!!
        ... I'd tried to declare arrays and had gotten error messages. To get past that, I'd started declaring arrays using splice. ... since it worked I continued to do it that way.

        This strikes me as a perfect example of the Skinnerian process of shaping "superstitious" behavior.

        Please do not take this the wrong way: I do not mean it to be in any way offensive or a personal attack. If Skinner's analysis is right, "superstitious behavior" of this kind is something we all exhibit, know it or not, and always have and always will as long as we remain organisms whose behavior is predominantly operant. However, with self-awareness, we can occasionally eliminate behaviors that are wasteful of time or effort, or are actively destructive — and so you have!

        Look at Perl like a powerful tool which can do much good but also cause much destruction.

        It's no good for try and error hacking!!!

        Better get a good book and try to understand step by step what you are doing!

        Please don't try pressing every possible button to get only seemingly correct results...

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: element of an array of arrays
by AnomalousMonk (Archbishop) on Jun 17, 2013 at 17:16 UTC
    UPDATE ... populate a look-back array with rows from the original data. ... The flow concept goes like this:
      run through the original data via foreach. For every original data row :
        while there are less than 15 rows in the look-back array, push each original data row onto the look-back array
        after there are 15 rows in the look-back array, scan the look-back array for the specified value,
        then shift the first row off of the look-back array and push the current original data row onto the look-back array
        repeat until the end of the original data

    Is this flow concept so far off ...?

    Actually, that seems like a nice approach — assuming you understand what's really in the rows you're dealing with!

    Of course, there are always Other Ways To Do It. If the data in a typical row was very large (megabytes) and you really needed to maximize speed and so to minimize data movement, an approach based solely on array references and manipulation of array indices might be productive.

    >perl -wMstrict -le "my @ra = (0, 1, 42, 3, 4, 5, 6, 7, 8, 42, 10, 42, 12, 42); ;; for my $i (0 .. $#ra) { look_back_5_for_42(\@ra, $i); } ;; sub look_back_5_for_42 { my ($ar, $current_i) = @_; ;; my $back = $current_i - 5 + 1; $back = 0 if $back < 0; ;; printf qq{at index $current_i: }; for my $i ($back .. $current_i) { printf qq{42 at index $i, } if $ar->[$i] == 42; } print ''; } " at index 0: at index 1: at index 2: 42 at index 2, at index 3: 42 at index 2, at index 4: 42 at index 2, at index 5: 42 at index 2, at index 6: 42 at index 2, at index 7: at index 8: at index 9: 42 at index 9, at index 10: 42 at index 9, at index 11: 42 at index 9, 42 at index 11, at index 12: 42 at index 9, 42 at index 11, at index 13: 42 at index 9, 42 at index 11, 42 at index 13,