tags:

views:

121

answers:

2

Is there a Perl idiom for finding the item in an array that meets a specific criteria if there is one?

my $match = 0;
foreach(@list){
   if (match_test($_)){
      $result = $_;
      $match = 1;
      last;
      }
   }
$match || die("No match.");
say $result, " is a match.";

The example seems a bit awkward. I expect Perl to have something to handle this more cleanly.

+14  A: 

Yes, grep is what you are looking for:

my @results = grep {match_test($_)} @list;

grep returns the subset of @list where match_test returned true. grep is called filter in most other functional languages.

if you only want the first match, use first from List::Util.

use List::Util qw/first/;

if (my $result = first {match_test($_)} @list) {
    # use $result for something
} else {
    die "no match\n";
}
Eric Strom
Also, the expression [`@array ~~ $scalar`](http://perldoc.perl.org/perlsyn.html#Smart-matching-in-detail) is true when `$scalar` is in `@array`.
daxim
@daxim => assuming you have a perl new enough to have smartmatching (5.10+)
Eric Strom
@C.W.Holeman II: I gave an example of both cases in my answer.
Ether
@daxim et al: From 5.10.1+ the order of ~~ is important. Thus it needs to be `$scalar ~~ @array` NB. To help I think of ~~ as synonym for "in".
draegtun
+6  A: 

If there could be multiple matches:

 my @matches = grep { match_test($_) } @list;

If there could only be one match, List::Util's 'first' is faster (assuming a match is found):

 use List::Util 'first';
 if (my $match = first { match_test($_)} @list)
 {
      # do something with the match...
 }
Ether
faster on very large arrays, but that's not the common case
ysth
It's also no faster if there are zero results, since every element still needs to be checked.
Ether