Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Printing from three arrays

by oysterperl (Novice)
on Sep 26, 2019 at 08:33 UTC ( [id://11106723]=perlquestion: print w/replies, xml ) Need Help??

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

Hi, I have three arrays with elements shown below: array1 = 1,2,3 array2 = a,b,c,d,e,f,g array3 = I,II,III I want it displayed like this:
1,a,I 2,a,I 3,a,I 4,a,I 5,a,I 1,a,II 2,a,II 3,a,II 4,a,II 5,a,II 1,a,III 2,a,III 3,a,III 4,a,III 5,a,III 1,b,I 2,b,I 3,b,I 4,b,I 5,b,I 1,b,II 2,b,II 3,b,II 4,b,II 5,b,II 1,b,III 2,b,III 3,b,III 4,b,III 5,b,III and so on.
I am trying the following but its not able to loop correctly.
my $i = $j = $k = 0; for my $i (@array1) { for my $j (@array2) { for my $k (@array3) { print "$array1[k],$array2[j],$array3[i],\n"; $k++; } $j++; } $k++; }
How can I fix this? Thanks in advance for your help.

Replies are listed 'Best First'.
Re: Printing from three arrays
by hippo (Bishop) on Sep 26, 2019 at 08:49 UTC

    If you aren't doing anything else with them, just loop over the elements themselves instead of the indices. The innermost loop will loop most quickly, of course. So:

    #!/usr/bin/env perl use strict; use warnings; my @array1 = (1, 2, 3); my @array2 = (qw/a b c/); # Enough to test my @array3 = (qw/I II III/); for my $alpha (@array2) { for my $roman (@array3) { for my $arabic (@array1) { print "$arabic,$alpha,$roman\n"; } } }
      Nested loops become unwieldy when you have many arrays. With a suitable module it does not matter how many dimension the input has:
      use 5.010; use Set::Scalar qw(); my $iter = Set::Scalar->cartesian_product_iterator( map { Set::Scalar->new(@$_) } [1, 2, 3], [qw/a b c/], [qw/I II III/], ); while (my @m = $iter->()) { say "@m"; }
      There are numerous alternatives: https://metacpan.org/search?q=cartesian+product
      Bonus:
      use v6; .say for cross (1, 2, 3), <a b c>, <I II III>
Re: Printing from three arrays
by Discipulus (Canon) on Sep 26, 2019 at 08:55 UTC
    Hello oysterperl and welcome to the monastery and to the wonderful world of perl!

    You start with @array1 but I think is better to cycle @array2 which seems governing your output.

    use strict; use warnings; my @array1 = (1..5); my @array2 = ('a','b','c','d','e','f','g'); my @array3 = ('I', 'II', 'III'); foreach my $letter(@array2){ foreach my $index_roman(0..$#array3){ foreach my $index (0..$#array1){ print "$array1[$index],$letter,$array3[$index_roman]\n"; } } }

    PS as wisely said by hippo, cycling indexes is no more needed, so semplified nested loop will be:

    foreach my $letter(@array2){ foreach my $roman(@array3){ foreach my $number (@array1){ print "$number,$letter,$roman\n"; } } }

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Printing from three arrays
by johngg (Canon) on Sep 26, 2019 at 14:29 UTC

    Another alternative to looping is to use the glob built-in.

    johngg@shiraz:~/perl/Monks$ perl -Mstrict -Mwarnings -E ' my @arr1 = qw{ 1 2 3 }; my @arr2 = q{a} .. q{g}; my @arr3 = qw{ I II III }; my $globStr = qq{{@{ [ join q{,}, @arr1 ] }}} . qq{{@{ [ join q{,}, @arr2 ] }}} . qq{{@{ [ join q{,}, @arr3 ] }}}; say $globStr; say for glob $globStr;' {1,2,3}{a,b,c,d,e,f,g}{I,II,III} 1aI 1aII 1aIII 1bI 1bII 1bIII 1cI 1cII 1cIII 1dI 1dII 1dIII 1eI 1eII 1eIII 1fI 1fII 1fIII 1gI 1gII 1gIII 2aI 2aII 2aIII 2bI 2bII 2bIII 2cI 2cII 2cIII 2dI 2dII 2dIII 2eI 2eII 2eIII 2fI 2fII 2fIII 2gI 2gII 2gIII 3aI 3aII 3aIII 3bI 3bII 3bIII 3cI 3cII 3cIII 3dI 3dII 3dIII 3eI 3eII 3eIII 3fI 3fII 3fIII 3gI 3gII 3gIII

    I hope this is of interest.

    Update : AnomalousMonk quite rightly points out that I'm the idiot who didn't read the question properly, a failing that has dogged me since O- and A-levels at school :-(

    Getting the left-hand value to circulate the fastest and inserting the required commas involves some jiggery-pokery to insert marker text into a re-ordered $globStr and a split, reverse and join in a map for each string generated by glob. Probably more trouble than it's worth.

    johngg@shiraz:~/perl/Monks$ perl -Mstrict -Mwarnings -E ' my @arr1 = qw{ 1 2 3 }; my @arr2 = q{a} .. q{g}; my @arr3 = qw{ I II III }; my $globStr = qq{{@{ [ join q{,}, @arr3 ] }}} . q{__} . qq{{@{ [ join q{,}, @arr2 ] }}} . q{__} . qq{{@{ [ join q{,}, @arr1 ] }}}; say $globStr; say for map { join q{,}, reverse split m{__} } glob $globStr;' {I,II,III}__{a,b,c,d,e,f,g}__{1,2,3} 1,a,I 2,a,I 3,a,I 1,b,I 2,b,I 3,b,I 1,c,I 2,c,I 3,c,I 1,d,I 2,d,I 3,d,I 1,e,I 2,e,I 3,e,I 1,f,I 2,f,I 3,f,I 1,g,I 2,g,I 3,g,I 1,a,II 2,a,II 3,a,II 1,b,II 2,b,II 3,b,II 1,c,II 2,c,II 3,c,II 1,d,II 2,d,II 3,d,II 1,e,II 2,e,II 3,e,II 1,f,II 2,f,II 3,f,II 1,g,II 2,g,II 3,g,II 1,a,III 2,a,III 3,a,III 1,b,III 2,b,III 3,b,III 1,c,III 2,c,III 3,c,III 1,d,III 2,d,III 3,d,III 1,e,III 2,e,III 3,e,III 1,f,III 2,f,III 3,f,III 1,g,III 2,g,III 3,g,III

    Cheers,

    JohnGG

      IIRC, glob iterates most slowly over its first (leftmost) iteration group,  {1,2,3} in this case, and more rapidly over each succeeding iteration group, so that the the last (rightmost) group  {I,II,III} iterates most quickly. This is the behavior I see in the output of your example code: the first (leftmost, arabic-number) field or column of each  "1aI" output record/string changes most slowly, the second (middle, alpha) changes more quickly, and the third (rightmost, roman-number) column changes fastest.

      However, oysterperl, as shown in the example of desired output in the OP, seems to want the first (leftmost, arabic-number) column to change most quickly, the second (alpha) column to change most slowly, and the third (roman-number) column to change at an intermediate rate. Is it possible to produce this result with a glob solution? (The OPed example output also has commas separating the columns, but this is easily done with a glob template.)


      Give a man a fish:  <%-{-{-{-<

        I checked the glob page, and I could not find something that would make it change it's ways, so you would need to split and join it :(

        perl -e '@_=split(/=/, $_) and print join(", " ,@_[2,0,1]),"\n" for glob "{a,b,c}={I,II,III}={1,2,3,4}"'

        it's cumbersome, though. I tried something with unshift pop but it was longer.

        perl -E '@_=split /=/ and unshift(@_,pop @_) and say join ", ", @_ for glob "{a,b,c}={I,II,III}={1,2,3,4}"'

        perl -E '@_=split /=/ and say join ", ", unshift(@_, pop @_) && @_ for glob "{a,b,c}={I,II,III}={1,2,3,4}"'

Re: Printing from three arrays
by BillKSmith (Monsignor) on Sep 26, 2019 at 22:47 UTC
    You should start every perl program with:
    use strict; use warnings;

    The purpose is to help you solve you own problems. In this case, you will get several error messages. Some of them will not make any sense to you. Fix the easy ones and try again. You probably have fixed more than you thought. You may have also revealed a few more errors. Keep fixing errors a few at a time until it runs. If you really get stuck, ask for explanation of your remaining errors. Your current script probably will not run correctly even when it does run. You may have to ask for help again. It is much easier for the monks to find a fix you logic errors when they are not masked by other errors.

    Another hint is to use meaningful variable names. Note the improvement in this solution.

    use strict; use warnings; my @digits = qw(1 2 3); my @letters = qw(a b c d e f g); my @romans = qw(I II III); foreach my $letter (@letters) { foreach my $roman (@romans) { foreach my $digit (@digits) { print "$digit,$letter,$roman\n"; } } }
    Bill
Re: Printing from three arrays
by FreeBeerReekingMonk (Deacon) on Sep 26, 2019 at 21:06 UTC
    edit:Deleted code that was identical to Discipulus's code.

    You want to loop over the array index, you need to enumerate from 0 to the array length minus 1, which is that "$#". The initialization (my $i = $j = $k = 0;) is not required because you already use the "my" in each loop.

    A more C stylish approach (not necessarily better):

    #!/usr/bin/env perl use strict; use warnings; my @array1 = (1, 2, 3); #veryfast my @array2 = (qw/a b c/); # Enough to test #veryslow my @array3 = (qw/I II III/); # my $i = $j = $k = 0; for(my $i=0;$i<=$#array2;$i++) { for(my $j=0;$j<=$#array3;$j++) { for(my $k=0;$k<=$#array1;$k++) { print "$array1[$k],$array2[$i],$array3[$j],\n"; } } }

    Note that because in each iteration it needs to calculate $# these loops are slower than iterating over the values of each array, as already shown by other monks.

Re: Printing from three arrays
by oysterperl (Novice) on Sep 30, 2019 at 06:52 UTC
    Thank you, all. This worked like a charm. Very useful and informative discussion as well. Appreciate your help!

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (None)
    As of 2024-04-25 00:02 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found