Perl: the Markov chain saw PerlMonks

How to compare hash values within the same hash?

by phildeman (Sexton)
 on Sep 24, 2013 at 22:24 UTC Need Help??
phildeman has asked for the wisdom of the Perl Monks concerning the following question:

Is there a way to compare the values of one hash (while looping through the hash) and determine how many times
a value has occurred in the hash?

\$hash = (
key1 => 10,
key2 => 10,
key3 => 3,
key4 => 5,
key5 => 10
);

In the hash, above, there are 3 occurrences of the value 10. I need to do this in order display the value
that has the highest frequency. Perhaps, there is a way to loop through the hash to compare the current
value to the previous value, then increment a counter variable by 1?

Any suggestions?

Thanks.
• Comment on How to compare hash values within the same hash?

Replies are listed 'Best First'.
Re: How to compare hash values within the same hash?
by choroba (Chancellor) on Sep 24, 2013 at 22:39 UTC
Attention - when assigning to a hash, start its name with the percent sigil, not dollar:
```%hash = (
key1 => 10,
# ...
);

To count numbers of occurrences, hashes are usually used:

```my %freq;
my (\$max_freq, \$max_value) = 0;
for my \$value (values %hash) {
\$freq{\$value}++;
if (\$freq{\$value} > \$max_freq) {
\$max_freq  = \$freq{\$value};
\$max_value = \$value;
}
}
print "\$max_value: \$max_freq times\n";
لսႽ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: How to compare hash values within the same hash?
by LanX (Chancellor) on Sep 24, 2013 at 23:53 UTC
```  DB<100> %hash = (
key1 => 10,
key2 => 10,
key3 => 3,
key4 => 5,
key5 => 10
);
=> ("key1", 10, "key2", 10, "key3", 3, "key4", 5, "key5", 10)

DB<101> \$count{\$_}++ for values %hash
=> ""

DB<102> \%count
=> { 3 => 1, 5 => 1, 10 => 3 }

DB<106> (\$max) = reverse sort values %count
=> 3

DB<109> grep { \$count{\$_} == \$max } %count
=> 10

this will give you a list of all values which are maximal, not only one.

update

otherwise, if only one value is enough:

```  DB<119> %rcount= reverse %count
=> (3, 10, 3, 10)

DB<120> \%rcount
=> { 1 => 3, 3 => 10 }

DB<121> \$rcount{\$max}
=> 10

and here an easier approach to find the maxmimum.

```  DB<113> use List::Util qw/max/

DB<114> \$max = max values  %count
=> 3

Cheers Rolf

( addicted to the Perl Programming Language)

Re: How to compare hash values within the same hash?
by roboticus (Chancellor) on Sep 24, 2013 at 23:58 UTC

I like to invert the hash for things like this. This way, you get a list of the keys for each value:

```\$ cat t.pl
#!/usr/bin/perl

my %hash = (key1=>10,key2=>10,key3=>3,key4=>5,key5=>10);

my %invhash;
while (my (\$k,\$v) = each %hash) {
push @{\$invhash{\$v}}, \$k;
}

for my \$v (sort {\$a<=>\$b} keys %invhash) {
print "\$v cnt=", scalar(@{\$invhash{\$v}}),
" keys=",join(", ",@{\$invhash{\$v}}), "\n";
}

\$ perl t.pl
3 cnt=1 keys=key3
5 cnt=1 keys=key4
10 cnt=3 keys=key5, key2, key1

But if you don't find the list of keys useful, or if there's too much data, just incrementing a counter as you suggest will do the job.

...roboticus

When your only tool is a hammer, all problems look like your thumb.

Re: How to compare hash values within the same hash?
by Anonymous Monk on Sep 25, 2013 at 03:11 UTC

```# Frequency of each value.
my %freq;
\$freq{\$_}++ for values %hash;

# Sort by frequency.
my @bucket;
@bucket[values %freq] = keys %freq;

print "There are \$#bucket occurrences of the value \$bucket[-1].\n";
Re: How to compare hash values within the same hash?
by Marshall (Abbot) on Sep 25, 2013 at 10:00 UTC
I think you want the peg counts (3rd printout below), but I showed some other stuff just for fun.
```#!usr/bin/perl -w
use strict;
#http://perlmonks.org/?node_id=1055552

my %hash =
(
key1 => 10,
key3 => 3,
key2 => 10,
key4 => 5,
key5 => 10,
key6 => 3,
);

# The keys don't matter.
# What matters is the histogram of the values.

my %histo;    #histogram of each key's summary data
my %pegs;     #peg count's of each key

#a bit brain boggling, but it works for a sum...
#the (my \$value's) are pre-computed at start of the loop
#

foreach my \$value (values %hash)
{
\$pegs{\$value}++;
\$histo{\$value}+=\$value;
}

#
# various print formats...
#
print "histo sort by key:\n";
foreach my \$x (sort {\$a <=> \$b} keys %histo)
{
print "\$x => \$histo{\$x}\n";
}

print "\nhisto sort by inverse value:\n";
foreach my \$x (sort {\$histo{\$b} <=> \$histo{\$a}} keys %histo)
{
print "\$x => \$histo{\$x}\n";
}

print "\nPeg counts of values\n";
foreach my \$x (sort {\$pegs{\$a} <=> \$pegs{\$b}} keys %pegs)
{
print "\$x => \$pegs{\$x}\n";
}

=summary of prints:

sort by key of sums:
3 => 6   #keys 3,6
5 => 5   #keys 5
10 => 30 #keys 1,2,5

sort by inverse value of sums: (note swap of \$b and \$a)
#I did that to make it a bit more interesting...
10 => 30
3 => 6
5 => 5

Peg counts of values: (value and number of times seen)
5 => 1
3 => 2
10 => 3
=cut
There are a number of ways to present the max (eg 10 => 3) peg count, but what do you want if say 3=>3 also?

Create A New User
Node Status?
node history
Node Type: perlquestion [id://1055552]
Approved by ww
Front-paged by MidLifeXis
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (8)
As of 2017-08-22 12:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
Who is your favorite scientist and why?

Results (335 votes). Check out past polls.

Notices?