Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

How do you print the key(s) of the largest value in a hash? I have a script that counts the occurences of something and the occurences it the value of that key.
Key -> Value red -> 2 pink -> 1 orange -> 4 black -> 3 blue -> 4
And I need it to determine the greatest value, sort by it and only print the highest keys/value. In this example the highest value is 4 shared by ORANGE and BLUE. I need it so it just prints these two hash elements.

Whether it's just one highest value or if X number share it, all the highest valued key/value pairs need to be sorted.

I am very confused at how to do this.

Replies are listed 'Best First'.
Re: printing largest hash value
by BrowserUk (Pope) on Feb 22, 2005 at 04:39 UTC

    Is there any Perl program that isn't simplified using List::Util? Why it never made it into the core, when so much other dross has....

    #! perl -slw use strict; use List::Util qw[ max ]; my %hash = ( red => 2, pink => 1, orange => 4, black => 3, blue => 4 ); my $max = max values %hash; print "$_ => $hash{ $_ }" for sort grep{ $hash{ $_ } == $max } keys %h +ash; __END__ P:\test>433230 blue => 4 orange => 4

    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.
      Why it never made it into the core...
      Actually, it did! It's been core since 5.72. Check perl572delta and perl58delta (not sure if these links'll work, I can't seem to reach perldoc.com from where I am).

        {glub!} <realsmall>I knew it was a good idea :{</realsmall>


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.
Re: printing largest hash value
by brian_d_foy (Abbot) on Feb 22, 2005 at 04:06 UTC

    Not that there is anything wrong with the other solutions, but here is another one. I sort the keys by values, then use a while loop to figure out where the highest value ends in the list of sorted keys, @keys. Once I know that, I slice @keys to get the right keys.

    #!/usr/bin/perl my %h = ( red => 2, pink => 1, orange => 4, black => 3, blue => 4, green => 3, ); my $i = 0; my @keys = sort { $h{$b} <=> $h{$a} } keys %h; 1 while( $h{$keys[++$i]} == $h{$keys[0]} ); my @largest = @keys[0..$i-1]; print qq|Largest are "@largest"\n|;

    I had a solution that used grep, but that's stupid since I don't need to go through the rest of the elements once I know I've seen the highest ones.

    #!/usr/bin/perl use strict; my %h = ( red => 2, pink => 1, orange => 4, black => 3, blue => 4, ); my @keys = sort { $h{$b} <=> $h{$a} } keys %h; my @largest = grep { $h{$_} == $h{$keys[0]} } @keys; print qq|Largest are "@largest"\n|;
    --
    brian d foy <bdfoy@cpan.org>
Re: printing largest hash value
by thedoe (Monk) on Feb 22, 2005 at 03:42 UTC

    Why not just do the simple way:

    my %hash = ( red => 2, pink => 1, orange => 4, black => 3, blue => 4 ); my ($max, @maxKeys); for (keys %hash) { $max = $hash{$_} unless $max; if ($hash{$_} > $max) { @maxKeys = ($_); $max = $hash{$_}; } elsif ($hash{$_} == $max) { push @maxKeys, $_; } } print "$_: $hash{$_}\n" for @maxKeys;

    This will only touch each value once, which is necessary anyway for this type of problem, giving you a O(N) solution.

    Update: My bad, didn't see the part about printing the keys and values, so I have updated my code accordingly.

Re: printing largest hash value
by sulfericacid (Deacon) on Feb 22, 2005 at 03:10 UTC
    This is untested but should give you an idea. There is definitely more efficent ways to do this and it'd be interesting to see other people's methods on doing this.
    my $max_count = 0; foreach my $key (keys %hash) { my $count = $hash{$key}; if ($count > $max_count) { $max_count = $count; } } foreach my $key (keys %hash) { my $count = $hash{$key}; if ($count == $max_count) { print "$key $count\n"; } }


    "Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

    sulfericacid
Re: printing largest hash value
by demerphq (Chancellor) on Feb 22, 2005 at 09:06 UTC
    my %hash=(); my $highest; my @highest=grep {defined $highest ? $hash{$_}==$highest : (($highest= +$hash{$_}),1) } sort { $hash{$b} <=> $hash{$a} || $a cmp $b } keys %hash;

    or

    my %hash=(); my %rev; my $highest=0; for (keys %hash) { $rev{$hash{$_}}{$_}++; $highest=$hash{$_} if $highest < $hash{$_}; } print sort keys %{$rev{$highest}};
    ---
    demerphq

Re: printing largest hash value
by saskaqueer (Friar) on Feb 22, 2005 at 03:13 UTC

    There's probably a better solution to this, but the coffee isn't holding out for my brain right now:

    #!/usr/bin/perl -w use strict; my %hash = ( red => 2, pink => 1, orange => 4, black => 3, blue => 4 ); my %reverse; while ( my ($k, $v) = each %hash ) { push( @{$reverse{$v}}, $k ) } my @highest = @{ @reverse{ ( sort { $b <=> $a } keys %reverse )[0] } }; print join(', ', @highest), $/;

    If you also need to know the value of the keys that have the highest value, here's a little rewrite that also makes the whole operation look simpler:

    #!/usr/bin/perl -w use strict; my %hash = ( red => 2, pink => 1, orange => 4, black => 3, blue => 4 ); my %reverse; while ( my ($k, $v) = each %hash ) { push( @{$reverse{$v}}, $k ) } my $high_val = ( sort { $b <=> $a } keys %reverse )[0]; my @highest = @{ $reverse{$high_val} }; print "highest value: $high_val\n", "keys with this value: ", join(', ', @highest), "\n";
Re: printing largest hash value
by RiotTown (Scribe) on Feb 22, 2005 at 03:33 UTC
    #!/usr/bin/perl my %hash = ( red => 2, pink => 1, orange => 4, black => 3, blue => 4 ); foreach ( reverse sort { $b <=> $a } keys %hash ) { if ( !$highest || $hash{$_} == $highest ) { print "\t$_, $hash{$_}\n"; $highest = $hash{$_}; } }

      Avoid rearranging the elements in an array you've just sorted.

      Instead of reverse sort { $b <=> $a } keys %hash, write sort { $a <=> $b } keys %hash. The second approach reads more clearly and runs more quickly.