tags:

views:

1011

answers:

5

In Perl both grep and map take an expression and a list, and evaluate the expression for each element of the list.

What is the difference between the two?

+14  A: 

grep returns those elements of the original list that match the expression, while map returns the result of the expression applied to each element of the original list.

$ perl -le 'print join " ", grep $_ & 1, (1, 2, 3, 4, 5)'
1 3 5
$ perl -le 'print join " ", map $_ & 1, (1, 2, 3, 4, 5)'
1 0 1 0 1

The first example prints all the odd elements of the list, while the second example prints a 0 or 1 depending on whether the corresponding element is odd or not.

Greg Hewgill
Additionally, map allows one-to-many mapping, so its expression is in list context, not the scalar context of grep's expression.
ysth
@ysth: That's true. I was going to say something about map always returning the same number of elements as its list argument, but then remembered that the expression could return none or more than one element as a list.
Greg Hewgill
+1  A: 

Think of grep as map with a filter. map iterates and provides an opportunity to do something with every item. For example these two lines are equivalent:

my @copy = @original;
my @copy = map {$_} @original;

Similarly, these two are equivalent:

my @copy = grep {-f $_} @original;

@copy = ();
for (@original)
{
  push @copy, $_ if -f $_;
}

grep provides the ability to insert a conditional, and therefore becomes a filter.

Paul Beckingham
+2  A: 

One other thing about grep: in a scalar context it tells you how many items it found. This can be useful if you don't really want a second list, but you do want to know how many items of a certain kind there are.

my @numbers = qw/1 2 3 4 5 6/;

my @odd_numbers  = grep { $_ & 1 } @numbers; # grep returns (1, 3, 5)

my $how_many_odd = grep { $_ & 1 } @numbers; # grep returns 3

Edit: Since the OP asked in a comment, I should say that you can use map in a scalar context in the same way. The point wasn't that grep is the only one of the two that can do this, but that it sometimes comes in handy to do this with grep.

Telemachus
doesn't map do the same in scalar context?
Nathan Fellman
Yes. I didn't mean to imply that only grep does this - just that it's a nice feature that people sometimes don't know. (Also, I can't really think of a case where I would want to use map in a scalar context.)
Telemachus
thanks for clarifying that :)
Nathan Fellman
+5  A: 

I find that it's helpful to think think about grep() and map() in their most general form:

grep {BLOCK} LIST   
map  {BLOCK} LIST

grep() is a filter: it returns the subset of items from LIST for which BLOCK returns true.

map() is a mapping function: send a value from LIST into BLOCK, and BLOCK returns a list of 0 or more values; the combined set of all of those calls to BLOCK will be the ultimate list returned by map().

FM
+1  A: 

map applies a function to all elements in a list and returns the result.

grep returns all elements in a list that evaluate to true when a function is applied to them.

my %fruits = (
  banana => {
    color => 'yellow',
    price => 0.79,
    grams => 200
  },
  cherry => {
    color => 'red',
    price => 0.02,
    grams => 10
  },
  orange => {
    color => 'orange',
    price => 1.00,
    grams => 225
  }
);

my %red_fruits = map { $_ => $fruits{$_} }
                   grep { $fruits{$_}->{color} eq 'red' }
                     keys(%fruits);

my @prices = map { $fruits{$_}->{price} } keys(%fruits);
my @colors = map { $fruits{$_}->{color} } keys(%fruits);
my @grams  = map { $fruits{$_}->{grams} } keys(%fruits);

# Print each fruit's name sorted by price highest to lowest:
foreach( sort { $fruits{$a}->{price} <=> $fruits{$b}->{price} keys(%fruits) )
{
  print "$_ costs $fruits{$_}->{price} each\n";
}# end foreach()
JDrago