Lists

grep#

Filter a list to the elements where the block or expression is true.

grep walks LIST from left to right, evaluates BLOCK or EXPR once per element with $_ aliased to that element, and returns the elements for which the test produced a true value. It is a list filter, not a loop — the list you get back is a subset of the list you passed in, in original order.

Synopsis#

grep BLOCK LIST
grep EXPR, LIST
my @kept  = grep { COND } @list;
my @match = grep /pattern/, @list;

The two forms are interchangeable in power; BLOCK is the general case, and EXPR is a convenience for one-expression tests. When EXPR is a bare regex (/.../, m/.../, or qr/.../), it is implicitly matched against $_ — this is the idiom behind grep /^#/, @lines.

What you get back#

  • List context: the filtered list, in original order, with each returned element an alias into the source list (see Global state and Edge cases).

  • Scalar context: the count of elements for which the test was true. Not a boolean — it is the size of what the list context would have produced, so 0 is false, any positive count is true.

my @keep  = grep { $_ > 0 } @nums;   # list context: filtered list
my $count = grep { $_ > 0 } @nums;   # scalar context: count

Global state it touches#

  • $_ is aliased to each element of LIST for the duration of one BLOCK or EXPR evaluation. On exit from grep, $_ is restored to whatever it held before the call. Because the alias is to the real element — not a copy — assigning to $_ inside the block mutates the source list in place. This is the same contract as map and as a for loop’s index variable.

  • The bare-regex form of EXPR reads and writes all the usual match-related magic variables ($1..$9, $&, ${^MATCH}, @+, @-, %+, %-, pos) on each iteration, exactly as if you had written the match longhand.

Examples#

Filter out undefined values:

my @vals    = (1, undef, 2, undef, 3);
my @defined = grep { defined $_ } @vals;
# @defined = (1, 2, 3)

Regex filter via the expression form — the bare regex matches against $_:

my @lines    = ("# comment", "code", "# another", "more code");
my @code     = grep !/^#/, @lines;       # drop comments
my @comments = grep  /^#/, @lines;       # keep comments

Custom test via the block form, when the condition is more than one expression:

my @users    = (
    { name => "alice", active => 1, age => 30 },
    { name => "bob",   active => 0, age => 25 },
    { name => "carol", active => 1, age => 17 },
);
my @adults_active = grep {
    $_->{active} && $_->{age} >= 18
} @users;

Count without building the list — use scalar context:

my @errors = ("ok", "fail", "ok", "fail", "fail");
my $nfail  = grep { $_ eq "fail" } @errors;   # 3
if (grep { $_ eq "fail" } @errors) {          # boolean use
    warn "had failures";
}

Remove blank and whitespace-only lines:

my @clean = grep { /\S/ } @lines;

Filter the keys of a hash by some property of the value:

my %scores  = (alice => 42, bob => 17, carol => 95);
my @passing = grep { $scores{$_} >= 50 } keys %scores;

Edge cases#

  • $_ is an alias, not a copy. Modifying $_ inside the block modifies the original list element. This is occasionally useful but more often a bug; write a read-only test:

    my @bumped = grep { $_++; $_ > 10 } @nums;   # MUTATES @nums
    

    If the elements are themselves not variables (e.g. literals, constants, or the result of a function call), assigning to $_ is a runtime error — “Modification of a read-only value”.

  • Returned elements are aliases too. Elements of the returned list share storage with the source list, just like the index variable of a for loop. Writing to them through the returned list propagates back:

    my @arr  = (1, 2, 3, 4);
    my @even = grep { $_ % 2 == 0 } @arr;
    $even[0] = 99;      # @arr is now (1, 99, 3, 4)
    

    This is rarely wanted. Copy explicitly with map { $_ } grep ... or a fresh my when you need independent storage.

  • Expression form vs. block form — precedence. grep EXPR, LIST uses a comma to separate the test from the list; grep BLOCK LIST does not. Mixing them trips you up:

    grep /x/, @arr;          # EXPR form, correct
    grep { /x/ } @arr;       # BLOCK form, correct
    grep { /x/ }, @arr;      # WRONG: comma after block is a syntax
                             # error or silently wrong depending on
                             # context
    
  • grep in chained expressions. grep takes a LIST, which means everything after the first argument is consumed as part of the list. Parenthesise when you want to follow grep with more terms:

    my @out = (grep { /x/ } @a), @b;   # concatenates filtered @a and @b
    my @bad =  grep { /x/ } @a,  @b;   # filters BOTH @a and @b together
    
  • Not a loop. grep is an expression, not a control structure. last, next, and redo inside the block target the nearest enclosing loop, not the grep itself — they will exit or restart whatever for/while the grep sits inside, not the grep. Use an early return from a helper sub if you need to short-circuit, or switch to List::Util first when all you want is the first match.

  • Empty input is empty output. grep over an empty list is an empty list in list context and 0 in scalar context. No special case needed.

  • Hash flattening. grep { ... } %h walks the hash’s alternating key/value flattening; the block sees keys and values interleaved in $_. This is almost never what you want; filter the keys and rebuild instead:

    my %filtered = map  { $_ => $h{$_} }
                   grep { some_test($_, $h{$_}) } keys %h;
    

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • map — same iteration contract with the same $_ alias semantics, but returns what the block produced for each element rather than the element itself

  • sort — reorder a list; often chained with grep as sort { ... } grep { ... } @list

  • any / all — short-circuiting boolean tests over a list from List::Util; use these instead of scalar grep when you only need a yes/no answer and the list is large or the test is expensive

  • first (from List::Util) — return the first element matching a block and stop; the right tool when grep is being used only to fetch one match

  • for / foreach — the looping construct with the same $_ aliasing rule; reach for it when you want side effects, not a filtered list

  • $_ — the default scalar that grep aliases on every iteration