Can I restrict grep (or map) to first match when I'm expecting only one match?

You can capture a single match by assigning to a scalar in list context

($regular) = map(/^regular\.roll (.*)$/, @lines);

The parentheses on the left hand side are important, otherwise you are imposing scalar context on the right hand size and the result will be something else, like the number of elements.

If you're trying to capture the first match from grep (but not map) and you are more comfortable using Perl modules, the first function in the List::Util package returns the first match, and is more efficient than calling grep and discarding all the extra matches.

use List::Util 'first';
...
$regular = first { /pattern/ } @input;

Note  See the end for how to stop right after the first match (one statement, with a module)


In order for the regex match operator to return the capture(s) themselves it indeed need be invoked in list context. But then you can form that list as you wish -- with just one scalar for instance, to catch only one from the returned list of scalars

my ($regular) = map { /^regular\.roll (.*)/ } @lines;

Here the ($v1, $v2,...) on the LHS provides the list context for the assignment operator, and with only one variable the first of the returned list of (.*) captures is assigned and the rest discarded.

This above has mostly been stated already but I think that it is important to comment on a few other things in the question as well.

  • Always have use warnings; and use strict; at the beginning of a program

  • An open statement must be tested for failure, and if it failed you print the error. Commonly

    open my $fh, '<', $file  or die "Can't open $file: $!";
    
  • I suggest to chomp in a separate statement

  • There is no reason for $ anchor in that regex (except with multiline string and /m modifier)

  • When printing, if you put it under quotes it's interpolated with spaces (see $,) in between

    say "@regular";
    

    or, print each element on its own line

    say for @regular;
    

    In order to be able to use say feature you need use feature qw(say);


Since only the first match is needed we'd rather not go through the rest of the list once a match is found. This can be achieved using first_result from List::MoreUtils (riffing off of mob's idea)

my $regular = firstres { my ($m) = /^regular\.roll (.*)/; $m } @lines;

The syntax inside the block is a little wordy but returning $1 after a lone regex didn't work for me (?). If having two statements is a bother this can be shortened, at the expense of readability

my $regular = firstres { ( /^regular\.roll (.*)/ )[0] } @lines;

where () around the regex provide for list context, and [0] takes the first element of that list. I added spaces around regex to try to alleviate that syntax a little; they aren't needed.


You could assign the results of the operation to a list that contains just one element:

my ($regular) = map(/^regular\.roll (.*)$/, @lines);
print $regular;

Tags:

Perl

Grep