Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

iterating through array and saving matches to new array

by jazzfan (Novice)
on Apr 15, 2018 at 06:27 UTC ( #1212906=perlquestion: print w/replies, xml ) Need Help??
jazzfan has asked for the wisdom of the Perl Monks concerning the following question:

I have contrived a problem to help me learn, iterating through an array 1 ..1000, if an item is > 850, save to new array which is visible outside the foreach block. I often find myself wanting to do similar things in while or if blocks but wonder if I am approaching this in the wrong way. So it's more a question of Perl style in handling such scenarios

our @newa; my @a = 1..1000; foreach my $elem (@a) { if ($elem > 850) { push @newa, $elem; }; say "newa is: @newa\n"; } say "newa is:", @newa;

Replies are listed 'Best First'.
Re: iterating through array and saving matches to new array
by poj (Monsignor) on Apr 15, 2018 at 06:40 UTC

    Take a look at grep

    #!/usr/bin/perl use strict; use Data::Dump 'pp'; my @a = 1..1000; my @newa = grep( $_>850,@a ); pp \@newa;
    poj
Re: iterating through array and saving matches to new array
by Marshall (Abbot) on Apr 15, 2018 at 13:28 UTC
    Probably what you wanted is "my @newa;" instead of "our @newa;". "our" is a special thing usually seen in a module. Read perl doc our for more info. In a module, a my variable cannot be exported, but an our variable can be.

    Yes, grep is the right tool here. I personally prefer the block form of grep and map.

    my @a = 1..1000; my @newa = grep{$_>850}@a;
    Grep is a "filter" operation.
Re: iterating through array and saving matches to new array
by karlgoethebier (Monsignor) on Apr 15, 2018 at 18:33 UTC

    A funny alternative:

    #!/usr/bin/env perl use strict; use warnings; use Iterator::Simple qw(iter igrep list); use Data::Dump; dd list ( igrep { $_ > 850 } iter([1..1000])); __END__

    Please see also Iterator::Simple.

    Best regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

    perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

Re: iterating through array and saving matches to new array
by learnedbyerror (Monk) on Apr 15, 2018 at 18:18 UTC

    With the replacement of our with my, your method, as a generalized method is fine.

    poj's use of grep is preferred when you have a simple criterion, such as your >850, as it will be faster than the generalized method.

      I am not sure about that!

      That idea of "faster" is true in Python's equivalent, but I'm not at all sure about that in Perl.

      In Perl, the foreach loop should run at about the same speed.

Re: iterating through array and saving matches to new array
by pwagyi (Beadle) on Apr 16, 2018 at 07:15 UTC

    As other monks had pointed out, use lexical scope (my over our). and grep is definitely suitable here. I'm not exactly sure why you need even grep/filtering if you want to find numbers between 850 and 1000? You could as well use.

    my $lower_bound = 850; my $upper_bound = 1000; my @newa = ($lower_bound+1 .. $upper_bound);

Re: iterating through array and saving matches to new array
by jazzfan (Novice) on Apr 15, 2018 at 20:05 UTC

    Thanks POJ, et al. Let me abstract the problem to be sure I understand it: If I want to save matches to new array, and make it visible outside the foreach (or while, if, etc) then you are proposing not creating the block in the first place, and in cases where you have to use a code block, and return the new array, just use "my" and not "our" appears to be the take away.

      ... where you have to use a code block, and return the new array, just use "my" and not "our" appears to be the take away.

      The preference of a lexical (my) variable over a package-global (our) variable has nothing to do with accessing a value generated within a code block (or lexical scope, as I would express it). The code in the OP using a package-global to return the values produced in the for-loop scope works just fine as far as it goes. The problem is that you are then left with a global variable. As the old punchline goes, now you've got two problems.

      Global variables are officially Frowned Upon because they are... well, global: they can be accessed from anywhere in your program and can be the source of "spooky action at a distance" problems, potentially very tricky to debug. Lexical variables have a well-defined and potentially extremely limited scope (the more limited, the better), and can (usually) be reasoned about and debugged more easily. Don't give yourself headaches.


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

Re: iterating through array and saving matches to new array
by jazzfan (Novice) on Apr 15, 2018 at 21:07 UTC

    And the nice benefit of grep is that it works with multiple arrays:

    my @b = 1..1000; my @c = 900..1000; my @d = 910..1000; my @newb = grep {$_>850}(@b&&@c&&@d);
      && imposes scalar boolean context on the first argument. Therefore, the grep on line 4 iterates over @d only provided @b and @c aren't empty. To iterate over several arrays, use comma:
      my @newb = grep $_ > 850, @b, @c, @d;

      It's also a good idea to test the code before posting here ;-)

      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
      ... it works with multiple arrays:
      ...
      my @newb =  grep {$_>850}(@b&&@c&&@d);

      I'm not sure what you mean by "works" in this context. Is the following output what you expect?

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "my @b = (1, 2, 3, 11, 12, 13); my @c = (4, 5, 6, 14, 15, 16); my @d = (7, 8, 9, 17, 18, 19); my @newb = grep { $_ > 10 } (@b && @c && @d); dd \@newb; " [17, 18, 19]
      Something seems to have happened to the contents of the  @b and  @c arrays. Again, did you intend this? This result comes from the short-circuiting behavior of the  && || and or logical operators; see perlop. (Please see the non-core module Data::Dump for the dd() function. The core Data::Dumper module has dumper functions that you will not need to install a module to access.)

      Update: Changed the array initializations in the example code above to make the point more clearly.


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

      To make the the "or" of all 3 arrays with unique values > 850, consider this:
      #!/usr/bin/perl use strict; use warnings; my @b = 1..1000; my @c = 900..1000; my @d = 910..1000; # my @newb = grep {$_>850}(@b&&@c&&@d); ### will not work! my %seen; my @newb = map{ ($_ > 850 and !$seen{$_}++) ? $_: ()} (@b,@c,@d); print "@newb\n";
      I am not sure what the intent here is.

        Wouldn't the code be more readable/maintainable/etc with List::Util::uniq():
            my @newb = uniq grep $_ > 850, @b, @c, @d;
        Results are the same per Test::More::is_deeply(). (In older Perls, see List::MoreUtils::uniq.)


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

Re: iterating through array and saving matches to new array
by jazzfan (Novice) on Apr 16, 2018 at 21:07 UTC

    No that was not what I meant by "working" - I posted my last comment too hastily, without sufficient testing.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1212906]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (3)
As of 2018-08-22 05:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Asked to put a square peg in a round hole, I would:









    Results (204 votes). Check out past polls.

    Notices?