http://www.perlmonks.org?node_id=1035181

I want to meditate on some of the most useful perl-isms that, while easy, are oft misunderstood for beginners. I say that, and am possibly projecting, since when I was a beginner, I had not grokked them and had misused them. I also want to give kudos to the comments below as they have greatly helped refine this posting.

Map, Grep and Sort are not the same thing, but are often used together. Flowing from right to left, they act like shell pipelining in reverse. They build something new, like a tiny little factory. While the original data structure is untouched, it should feel like a list is being transformed every step of the way.

Grep

I will start with grep as it is easier to understand. It is a filter in the purest sense. From the right, grep receives a list of things, one thing at a time, and either passes those things to the left or does not based on its logic. It's usage is

grep { filtering-block } @input_list

What is the filtering block? It is called for each item in the input list. If it returns a true value, that list element is passed to the left. If it returns a false value, the list element is not passed to the left. Inside the filtering block, the list element being examined is aliased to the variable $_. On the right, I see the input list. To the left is passed the filtered list.

my @caps = grep { $_ =~ /^[A-Z]/ } qw( This is for Edna );
Since the magic variable is implied, the above may be shortened to
my @caps = grep { /^[A-Z]/ } qw( This is for Edna );
Pipelining
It is common for grep and map to be chained together in a pipeline. Each part of the pipeline gets a list from the right and hands off a list to the left. The following snippet finds all words that start with a capitalized vowel. The first filter finds all words that start with a vowel and passes them to the left. It passes is and Edna to the left. The second filter then only passes Edna to the left.
my @vowel_caps = grep { /^[A-Z]/ } grep { /^[aAeEiIoOuU]/ } qw( This is for Edna );
The pipeline can consist of mixed and matched grep, map and sort pieces.
Sort
As it sounds, sorting takes a list from the right and passes a list of equal size to the left that is sorted by the sort block. The sort block is a function that compares variables $a and $b and returns -1, 0 or 1. The variables $a and $b are two items from the list that are to be sorted. If the return value is -1, that means $a should be sorted before $b. If the value is 1, $b should be sorted before $a. For a 0 return value, the order does not matter. For strings, the cmp operator can be used and for numbers the spaceship operator <=> is used. You can do two or more levels of sorting using the || operator.
my @numbers = sort { $b <=> $a } ( 23,5,23,64,2); my @words = sort { $b cmp $a } ( "this", "Is", "sortingly", "something" ); my @owords = sort ( "default", "operator", "is", "text", "sort" ); my @things = sort { $a->{order} <=> $b->{order} } ( { name => "foo", order => 4 }, { name => "bar", order => 88 }, { name => "baf", order => -12 } ); my @things = sort { $a->{order} <=> $b->{order} || $b->{name} cmp $a->{name} } ( { name => "foo", order => 4 }, { name => "bar", order => 88 }, { name => "baf", order => -12 }, { name => "zoo", order=>88 } );
Map
Map takes each item from the list on the right and passes the return value from its block to the left. It should not transform the items on the list as a matter of practice; it should not be used with side effects nor should it be used as the way to iterate through a list. Each item is aliased in block as the magic variable $_.
my @squares = map { $_ * $_ } ( 1,2,3,4,5,6 ); my @even_squares = map { $_ * $_ } grep { $_/2 == int($_/2) } ( 1,2,3,4,5,6 ); my @alpha_sorted_even_digits = sort { $a cmp $b } map { [ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" ]->[$_] } grep { $_/2 == int( $_/2 ) } ( 1,2,3,4,5,8,9 );
One important thing to note is that map operates in list content, meaning that it returns one or more things. This feature can be used to generate a list that is greater than the original list, or even to generate a hash.
my @doubled = map { $_, $_ } ( 1,3,4,5,6,6 ); my @flattened = map { @$_ } ( [ 1,2,3], [ "A","B","C" ], [ $var1, $var +2, $var3 ] ); my %squares_hash = map { $_ => $_ * $_ } ( 1,2,3,4,5,6 );
Beyond This being perl, there are many ways to do anything. There are a number of fantastic libraries set up for handling lists, namely