4

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.

Ether
  • 53,118
  • 13
  • 86
  • 159
CW Holeman II
  • 4,661
  • 7
  • 41
  • 72

2 Answers2

14

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
  • 39,821
  • 2
  • 80
  • 152
  • 6
    Also, the expression [`@array ~~ $scalar`](http://perldoc.perl.org/perlsyn.html#Smart-matching-in-detail) is true when `$scalar` is in `@array`. – daxim Jun 21 '10 at 17:33
  • 1
    @daxim => assuming you have a perl new enough to have smartmatching (5.10+) – Eric Strom Jun 21 '10 at 17:35
  • @C.W.Holeman II: I gave an example of both cases in my answer. – Ether Jun 21 '10 at 17:51
  • 3
    @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 Jun 22 '10 at 09:22
6

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
  • 53,118
  • 13
  • 86
  • 159