in reply to Counting unique elements in an array

Or you can use map although many do not like using map in a void context others find it more intuitive to operate on the entire array than looping through it's elements, Horses for courses. Map is slightly slower than foreach in this sort of case. If speed matters see this Re^4: how to avoid nested looping? for an excellent benchmark of map and for(each)? ).

my @array = qw/aa bb cc aa cc cc dd aa aa/; my %sums; map {$sum{$_}++} @array; print "$_ = $sums{$_}\n" foreach sort keys %sums;

Cheers,
R.

Pereant, qui ante nos nostra dixerunt!

Replies are listed 'Best First'.
Re^2: Counting unique elements in an array
by Roy Johnson (Monsignor) on Feb 08, 2005 at 17:04 UTC
    Here's a way that uses map in a non-void context:
    use Data::Dumper; my %counts; my @array = qw (aa bb cc aa aa cc cc dd aa aa); %counts = map {$_ => ++$counts{$_} } @array; print Dumper(\%counts);
    Here's another:
    my %counts = map { my $n; do { $n = (($last=$_) ... ($last ne $_)) } while $n =~ /E0$/; ($last => $n); } sort @array;
    Why they work is left as an exercise for the reader. ;-)

    Caution: Contents may have been coded under pressure.

      That is jumping through some hoops to avoid void. SuprisinglyUnsuprisingly (I just looked at it properly) the first one is a lot slower than the map in void context solution, the last one is slowest of all :)

      Updated

      added the for loop solution to the benchmark, it is nicely faster.
      #!/usr/bin/perl use strict; use warnings; use Benchmark; my @strings; my @array; push @strings, $_ x 2 for ("a" .. "z"); for (1..1000) { my $i=int rand $#strings; push @array, $strings[$i] } sub for_loop { my %sums; $sums{$_}++ foreach @array; # print "$_ = $sums{$_}\n" foreach sort keys %sums; } sub map_void { my %sums; map {$sums{$_}++} @array; # print "$_ = $sums{$_}\n" foreach sort keys %sums; } sub nonvoid_1 { my %sums; %sums = map {$_ => ++$sums{$_} } @array; # print "$_ = $sums{$_}\n" foreach sort keys %sums; } sub nonvoid_2 { my %sums; my $last=""; %sums = map { my $n; do { $n = (($last=$_) ... ($last ne $_)) } while $n =~ /E0$/; ($last => $n); } sort @array; # print "$_ = $sums{$_}\n" foreach sort keys %sums; } timethese( -3, {map_void => \&map_void, for_loop => \&for_loop, nonvoid_1 => \&nonvoid_1, nonvoid_2 => \&nonvoid_2 } ); __END__ Benchmark: running for_loop, map_void, nonvoid_1, nonvoid_2 for at lea +st 3 CPU seconds... for_loop: 3 wallclock secs ( 3.13 usr + 0.01 sys = 3.14 CPU) @ 15 +79.94/s (n=4961) map_void: 3 wallclock secs ( 3.19 usr + 0.00 sys = 3.19 CPU) @ 11 +65.83/s (n=3719) nonvoid_1: 3 wallclock secs ( 3.16 usr + 0.00 sys = 3.16 CPU) @ 33 +0.06/s (n=1043) nonvoid_2: 3 wallclock secs ( 3.28 usr + 0.00 sys = 3.28 CPU) @ 13 +9.33/s (n=457)

      Cheers,
      R.

      Pereant, qui ante nos nostra dixerunt!
        That is jumping through some hoops to avoid void.
        Well, yeah. I was posting them in sort of a humor/brainteaser vein rather than as a serious solution to a problem that is (as has been pointed out)
        1. probably homework, and
        2. a FAQ

        Incidentally, here's a method that benches about 9% faster than for_loop:

        sub new_guy { my %sums; ++$_ for @sums{@array}; }

        Caution: Contents may have been coded under pressure.
Re^2: Counting unique elements in an array
by revdiablo (Prior) on Feb 08, 2005 at 18:01 UTC
    although many do not like using map in a void context others find it more intuitive to operate on the entire array than looping through it's elements

    It's not that I "do not like" map in void context, I just think it's redundant with a plain for loop. Now, grabbing the return values from map is not redundant with for, so I use it in those cases. As for your specific example, I would write that map line as:

    $sum{$_}++ for @array;

    Which is shorter, contains less syntax, and puts the most important part of the code at the beginning of the line.