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

Hi Monks!

Trying to search for a value into this array ref using “ grep” to avoid using a loop in the code, It isn’t working, can anyone suggest where it can be fixed or done better?
Here is the code I am using to try this:
#!/usr/bin/perl use strict; use warnings; my $data = [ { 'date' => '20160101', 'day' => 'Friday' }, { 'date' => '20160215', 'day' => 'Monday' }, { 'date' => '20160530', 'day' => 'Monday' }, { 'date' => '20160704', 'day' => 'MOnday' }, { 'date' => '20160905', 'day' => 'Monday' }, { 'date' => '20161010', 'day' => 'Monday' }, { 'date' => '20161124', 'day' => 'Thursday' }, { 'date' => '20161125', 'day' => 'Friday' }, { 'date' => '20161226', 'day' => 'Monday' }, { 'date' => '20170102', 'day' => 'Monday' } ]; my $search = "20161010"; my $result = ( grep( /^$search$/, @{ $data } ) ) ? 'Found' : 'Not Foun +d'; print $result."\n";
Thank you!!

Replies are listed 'Best First'.
Re: Searching with grep
by choroba (Cardinal) on Mar 23, 2016 at 18:09 UTC
    grep iterates over the list you give it, assigns each member to $_ and evaluates the given expression or code. In this case, you try
    { date => '20161010', day => 'Monday' } =~ /^$search$/

    which clearly isn't true. Just modify your expression to match the date part of each element:

    my $result = ( grep $_->{date} =~ /^$search$/, @$data ) ? 'Found' : 'N +ot Found';

    Update: Note that for larger lists, any from List::Util might be faster if you just want to know whether such an element exists. It also specifies the intent more clearly.

    use List::Util qw{ any }; my $result = ( any { $_->{date} =~ /^$search$/ } @$data ) ? 'Found' : +'Not Found';

    ($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,
      Thanks for the info. I also noticed that if I use a reg exp. in the "grep", it will still match if this value:  my $search = "2016101";
      I changed the result line to this:
      my $result = ( grep $_->{date} eq $search, @{ $data } ) ? 'Found' : 'N +ot Found';
      My list isn't too big and it is one less module to load into to rest of the code.
      I like your "Choroba" whatever that means code:
      my $q; ($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,
Re: Searching with grep
by Marshall (Canon) on Mar 24, 2016 at 01:09 UTC
    You say: "to avoid using a loop in the code". You should understand there IS a loop here although disguised. It is a mistake to equate number of source code lines with performance. Sometimes more lines are "faster". In this case the grep will loop through all of the lines and report its result at the end.

    If performance is a concern and the hash is "large", some more wordy code could wind up being significantly faster on average because it can "give up" when a match is found.

    Update: oh, another point... $_->{date} =~ /^$search$/ That says that the date must exactly match the search, from beginning to end. Using "eq" is faster because it doesn't involve the regex engine. "eq" is a stupid, but a very fast critter. It will also "give up" at the first character position that mismatches.

    #!/usr/bin/perl use strict; use warnings; my $data = [ { 'date' => '20160101', 'day' => 'Friday' }, { 'date' => '20160215', 'day' => 'Monday' }, { 'date' => '20160530', 'day' => 'Monday' }, { 'date' => '20160704', 'day' => 'MOnday' }, { 'date' => '20160905', 'day' => 'Monday' }, { 'date' => '20161010', 'day' => 'Monday' }, { 'date' => '20161124', 'day' => 'Thursday' }, { 'date' => '20161125', 'day' => 'Friday' }, { 'date' => '20161226', 'day' => 'Monday' }, { 'date' => '20170102', 'day' => 'Monday' } ]; my $search = "20161010"; #[choroba] solution....and a good one! #my $result = ( grep $_->{date} =~ /^$search$/, @$data ) ? 'Found' : ' +Not Found'; #Another way as a demo using "last" my $found; foreach my $ref (@$data) { if ($ref->{date} eq $search) { $found++; print "Found $search\n"; last; ## stops the foreach loop after "found" } } print "Not Found: $search \n" if !$found;