create a hash of occurrences in an array with map

Viewed 205

I seem to recall that there was a "clever" way to create a hash from an array with Perl with map such that the keys are the elements of the the array and the values are the number of times the element appears. Something like this, although this does not work:

$ perl -e '@a = ('a','a','b','c'); %h = map { $_ => $_ + 1  } @a ; foreach $k (keys (%h)) { print "$k -> $h{$k}\n"}'
c -> 1
b -> 1
a -> 2
$

Am I imagining things? How can I do this?

2 Answers

I'm not sure this is what you are looking for, but you can easily do that with a for rather than a map:

$ perl -e '@a = ('a','a','b','c'); $h{$_}++ for @a; foreach $k (keys (%h)) { print "$k -> $h{$k}\n"}'
c -> 1
b -> 1
a -> 2

You can write map {$h{$_}++} @a ignoring its return value, but why would you do this? for (@a){$h{$_}++} is easy enough to type.

So why would you not do it?

map is meant for transforming lists. It takes an input list and generates an output list. It can confuse a reader if you use it in a different way using side effect instead of output.

Also, although map is optimized to not create the output list when called in void context, it is slower:

use warnings;
use strict;
use Benchmark qw/cmpthese/;
my @in = map {chr(int(rand(127)+1))} 1..10000;

my %out;
cmpthese(10000,
         {stmtfor => sub{%out = (); $out{$_}++ for @in},
          voidmap => sub{%out = (); map {$out{$_}++} @in;},
          }
     );

__END__

          Rate voidmap stmtfor
voidmap 2075/s      --    -17%
stmtfor 2513/s     21%      --
Related