Transforming a Perl array using map

The map function of Perl provides a simple way to transform a list of values to another list of values. Usually this is a one-to-one transformation but in general the resulting list can be also shorter or longer than the original list.

We saw that grep of Perl is a generalization of the UNIX grep command. It selects some (or all) of the elements from the original list and returns them unchanged.

map, on the other hand is used when you would like to make changes to the values of the original list.

The syntax is similar. You provide a block of code and a list of values, an array or some other expression that returns a list of values. For every value in the original list, the value is placed in $_ and the block is executed. The resulting values are passed on to the left-hand side of the assignment.

map for simple transformation


  my @numbers = (1..5);
  print "@numbers\n";       # 1 2 3 4 5
  my @doubles = map {$_ * 2} @numbers;
  print "@doubles\n";       # 2 4 6 8 10

Building fast look-up table

Sometimes we have a list of values but during the execution of the code we also need to check if a given value is within this list. We can use grep every time, to check if the current value is in the list. We can even use the any function from List::MoreUtils but it can be much more readable and faster if we use a hash for lookup.

We need to create a hash once, where the keys are the values from the array and the values of the hash are all 1. Then, a simple hash lookup can replace the grep.


  use Data::Dumper qw(Dumper);

  my @names = qw(Foo Bar Baz);
  my %is_invited = map {$_ => 1} @names;

  my $visitor = <STDIN>;
  chomp $visitor;

  if ($is_invited{$visitor}) {
     print "The visitor $visitor was invited\n";
  }

  print Dumper \%is_invited;

This is the output of the Dumper call:


  $VAR1 = {
            'Bar' => 1,
            'Baz' => 1,
            'Foo' => 1
          };

In this code we don't really care about the values of the hash elements, other than the fact that they should evaluate to true in Perl.

This solution is only interesting if you do repeated look-ups. Otherwise any or even grep will do it.

As you can see, in this example, for every element in the original array, map returned two values. The original value and 1.


  my @names = qw(Foo Bar Baz);
  my @invited = map {$_ => 1} @names;
  print "@invited\n"

will print:


  Foo 1 Bar 1 Baz 1

Complex expressions in map

You can put more complex expressions with map:


  my @names = qw(Foo Bar Baz);
  my @invited = map { $_ =~ /^F/ ? ($_ => 1) : () } @names;
  print "@invited\n"

Will print


  Foo 1

Inside the block we have a ternary operator that returns either a pair like earlier or an empty list. Apparently we only want to let people in if their name starts with an F.


  $_ =~ /^F/ ? ($_ => 1) : ()

perldoc

For further explanation with a couple of strange cases, check out perldoc -f map.


Perl tutorial

For further articles see the Perl tutorial.


In the comments, please wrap your code snippets within <pre> </pre> tags and use spaces for indentation.
blog comments powered by Disqus
Online courses:


Would you like to get
updated when I publish
the next article?

Follow me:

Google Plus Twitter RSS feed