Find the first element in an array in Perl that satisfies a condition
When you need to find the first element in an array that satisfies some condition, the first solution that might come to mind is to loop over all the elements using for and check them one by one. This would yield a working code, but there are nicer solution.
BTw if you are interested I've also written a solution to find the first matching element of a list in Python. It might be interesting to compare.
The condition is simple, the first animal that is longer than 5 characters.
First matching element using a for loop
examples/first_loop.pl
use strict; use warnings; use 5.010; my @animals = ('snake', 'camel', 'etruscan shrew', 'ant', 'hippopotamus', 'giraffe'); my $first; for my $animal (@animals) { if (length($animal) > 5) { $first = $animal; last; } } say $first; # etruscan shrew
In this solution we stop the iteration using the last statement when we found the first match.
First matching element using grep
People who are more experienced with Perl, might use the grep function.
examples/first_grep.pl
use strict; use warnings; use 5.010; my @animals = ('snake', 'camel', 'etruscan shrew', 'ant', 'hippopotamus', 'giraffe'); my ($first_with_grep) = grep { length($_) > 5 } @animals; say $first_with_grep;
It is much more compact, but it will not stop the operation on the first value and it will return the list of all the matching values. We use a pair of parentheses around a scalar variable on the left-hand-side of the assignment to store the first element from the returned list. So the solution is correct, but it is wasteful, especially if the original array is long and if the cost of each check is large. (Not in our case, but I trust you can imagine a longer array with more animals and a more complex condition.)
First element using the first function
The List::Util module provides a function called first that solves this problem. The syntax is exactly the same as with the grep function, but it will stop checking the condition after encountering the first element that meets the condition.
examples/first.pl
use strict; use warnings; use 5.010; use List::Util qw(first); my @animals = ('snake', 'camel', 'etruscan shrew', 'ant', 'hippopotamus', 'giraffe'); my $first_with_first = first { length($_) > 5 } @animals; say $first_with_first;
Compare grep and first
You might not trust me or the documentation that the first function stops as soon as possible, so I made some changes, moved the condition to a separate function that would also print each time it is called.
examples/first_log.pl
use strict; use warnings; use 5.010; use List::Util qw(first); my @animals = ('snake', 'camel', 'etruscan shrew', 'ant', 'hippopotamus', 'giraffe'); my ($first_with_grep) = grep { my_cond($_) } @animals; say $first_with_grep; say '---------'; my $first_with_first = first { my_cond($_) } @animals; say $first_with_first; sub my_cond { my ($str) = @_; say "length of $str"; return length($str) > 5; }
The result:
length of snake length of camel length of etruscan shrew length of ant length of hippopotamus length of giraffe etruscan shrew --------- length of snake length of camel length of etruscan shrew etruscan shrew
As you can see yourself, the solution using grep printed all the values from the array. The solution using first only printed up till the first matching element.
Etruscan shrew
In case you were wondering the Etruscan shrew is the smallest mammal by mass.
There is a shorter mammal, but this looks better.
Published on 2020-11-30