Re: Counting unique elements in an array
by davido (Cardinal) on Feb 08, 2005 at 11:00 UTC
|
my @array = qw/aa bb cc aa cc cc dd aa aa/;
my %sums;
$sum{$_}++ foreach @array;
print "$_ = $sums{$_}\n" foreach keys %sums;
| [reply] [d/l] |
Re: Counting unique elements in an array
by Random_Walk (Prior) on Feb 08, 2005 at 11:15 UTC
|
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!
| [reply] [d/l] [select] |
|
|
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.
| [reply] [d/l] [select] |
|
|
#!/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!
| [reply] [d/l] |
|
|
|
|
|
|
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.
| [reply] [d/l] [select] |
Re: Counting unique elements in an array
by murugu (Curate) on Feb 08, 2005 at 11:14 UTC
|
my %hash;
my @a=(1,2,2,3,3,3,3,3,1);
$hash{$_}++ for (@a);
print "$_\t$hash{$_}\n" for keys %hash;
| [reply] [d/l] |
Re: Counting unique elements in an array
by Limbic~Region (Chancellor) on Feb 08, 2005 at 13:36 UTC
|
Anonymous Monk,
This is one of the functions I was thinking about adding to RFC: Tool::Box. What you haven't said is if you want the output in any order, Asciibetical order, the order of first appearance, or some other order. Since no one has given you the "in the order of first appearance", here is my offer:
my @array = qw(bb aa dd cc aa aa cc aa aa cc);
print "$_\n" for uniq_ordered_count( \@array );
sub uniq_ordered_count {
my $list = shift;
my %count;
$count{ $_ }++ for @$list;
return map { exists $count{$_} ? "$_ - " . delete $count{$_} : ()
+} @$list;
}
Update: Per bart's recommendation. I changed the wording of original order to first appearance.
| [reply] [d/l] |
|
|
my @array = qw(bb aa dd cc aa aa cc aa aa cc);
print "$_ $_{$_}\n" for grep !$_{$_}++, @array;
Localize %_ if needed. | [reply] [d/l] |
Re: Counting unique elements in an array
by tirwhan (Abbot) on Feb 08, 2005 at 11:19 UTC
|
Similar to other solutions, but using grep and sort:
@array = qw (aa bb cc aa aa cc cc dd aa aa);
grep {$count{$_}++} @array;
print "$_ - $count{$_}\n" foreach sort keys %count;
Or search for "count unique array" in Super Search :-) | [reply] [d/l] |
|
|
I think you don't understand the 'grep' function. map in a void context I can understand, but grep? Doesn't make sense.
By the way, why are there so many replies to an obvious homework problem the answer to which can be found in an FAQ. Crazy!
| [reply] |
Re: Counting unique elements in an array
by perlsen (Chaplain) on Feb 08, 2005 at 11:53 UTC
|
@array = qw (aa bb cc aa aa cc cc dd aa aa);
@final = grep (!$same{$_}++, @array);
print "$_\-->$same{$_}\n" for keys %same;
output:
cc-->3
bb-->1
dd-->1
aa-->5
| [reply] [d/l] |
Re: Counting unique elements in an array
by tphyahoo (Vicar) on Feb 08, 2005 at 11:32 UTC
|
I would have described this differently. I think you want the *count* or the of each element in the array, not the sum. "Sum" makes me think of arithmetic. | [reply] |
| A reply falls below the community's threshold of quality. You may see it by logging in. |