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

Hey Folks, Relatively new to perl, trying to "push" the values from a loop into an array; but it only loops once and stores only the first value - what am I doing wrong here? here is the excerpt from the script:

my @store; foreach my $line (@$lines) { my @val = split(/\,/,$line); push (@store,@val[2]); } printf "@store\n";

Replies are listed 'Best First'.
Re: Store the outcome of a foreach loop in an array
by kcott (Archbishop) on Jan 11, 2019 at 05:19 UTC

    G'day perl_5eeker,

    Welcome to the Monastery.

    "Relatively new to perl, ..."

    You appear to have resolved your immediate problem. These are just a few tips you might find useful in the future.

    You can often avoid many intermediate variables. for, split, and many other functions, use $_ as a default variable. You can access an element of a list returned by a function directly like this: (function)[index]. So you could've pushed the data you wanted with one line; something like this:

    $ perl -E ' my @x = qw{a,b,c d,e,f g,h,i}; my @y; push @y, (split /,/)[1] for @x; say for @y; ' b e h

    Also, instead of calling push multiple times, you could've generated the wanted list with map and assigned it directly:

    $ perl -E ' my @x = qw{a,b,c d,e,f g,h,i}; my @y = map((split /,/)[1], @x); say for @y; ' b e h

    I think that second line would look somewhat cleaner as:

    my @y = map +(split /,/)[1], @x;

    However, using a unary plus for disambiguation seems to cause confusion for many. I'll leave you to decide whatever works best for you.

    That may, or may not, be useful in your current situation. Add it to your toolbox for possble future use.

    Finally, as I can't see it mentioned by anyone else, when working with CSV data, consider using Text::CSV. That's usually what I'd reach for first in that situation. Also, if you have Text::CSV_XS installed, it will run faster.

    — Ken

Re: Store the outcome of a foreach loop in an array
by haukex (Archbishop) on Jan 10, 2019 at 22:26 UTC

    The code you've showed appears to work fine for me - maybe $lines doesn't contain what you think it does? In this WebPerl live demo (requires a modern browser), I've shown how to use Data::Dumper to inspect data structures.

    You should probably also heed the warning that Perl gives you: "Scalar value @val[2] better written as $val[2] "- the former is a "slice" and is not the best way to access a single array element.

    Update: Code from WebPerl reproduced here:

Re: Store the outcome of a foreach loop in an array
by hippo (Archbishop) on Jan 10, 2019 at 23:07 UTC
    use strict; use warnings; use Test::More tests => 1; my $lines = [ 'c1r1,c2r1,c3r1,c4r1,c5r1', 'c1r2,c2r2,c3r2,c4r2,c5r2', 'c1r3,c2r3,c3r3,c4r3,c5r3' ]; my @store; foreach my $line (@$lines) { my @val = split (/,/, $line); push @store, $val[2]; } is_deeply (\@store, ['c3r1', 'c3r2', 'c3r3']);

    See How to ask better questions using Test::More and sample data.

    You didn't need to escape the comma in the regex. You don't need the brackets in the push statement. A single array element is better written with a $ sigil. That aside, your code (as demonstrated here) works fine.

Re: Store the outcome of a foreach loop in an array
by LanX (Saint) on Jan 10, 2019 at 22:23 UTC
    update

    Now that the OP updated code tags is obvious he didn't mean @val2 but @val[2]

    original

    You are pushing @val2 not @val.

    Better use strict and warnings to catch typos!

    Please put <code> </code> tags around your code and data

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      Thanks for your quick response @val2? - I meant to extract the 3rd column of the array "@val2".

        $val[2] gives the third element of @val.

        [link] creates a link outside code tags, that's why I misinterpreted your code.

        See also my update.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Store the outcome of a foreach loop in an array
by BillKSmith (Monsignor) on Jan 11, 2019 at 04:25 UTC
    Note: This is the type of transform that map was designed for.
    use strict; use warnings; my $lines = [ "c1r1,c2r1,c3r1,c4r1,c5r1\n", "c1r2,c2r2,c3r2,c4r2,c5r2\n", "c1r3,c2r3,c3r3,c4r3,c5r3\n", ]; my @store = map {(split /,/)[2]} @$lines; local $, = ",\n"; print @store;
    Bill
Re: Store the outcome of a foreach loop in an array
by pryrt (Abbot) on Jan 10, 2019 at 22:27 UTC

    I second what ++LanX said. But also, if the for loop is only going once, then @$lines only has one element. Are you sure $lines is an array-ref? Or did you really mean foreach my $line (@lines)?

      If I print "@$lines", I get this:

      c1r1,c2r1,c3r1,c4r1,c5r1 c1r2,c2r2,c3r2,c4r2,c5r2 c1r3,c2r3,c3r3,c4r3,c5r3
      I was trying to store "c3r1 c3r2 c3r3" in "@store" - but it only saves "c3r1" and comes out of the loop :(

        but it only saves "c3r1" and comes out of the loop

        This would happen if @$lines only has a single element, "c1r1,c2r1,c3r1,c4r1,c5r1\nc1r2,c2r2,c3r2,c4r2,c5r2\nc1r3,c2r3,c3r3,c4r3,c5r3". As I showed here, please use Data::Dumper to print out $lines, as in: "use Data::Dumper; $Data::Dumper::Useqq=1; print Dumper($lines);". If that shows a single string with \n's in it, then my assumption is correct. Instead of doing something like using split on that string, it's probably better if we took a look at where you're getting $lines from - please have a look at Short, Self-Contained, Correct Example, a SSCCE would be useful so we can run the code for ourselves and see exactly the issue you're having.

        Are you sure you showed us all the code?

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice