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

Hello again Perl Monks, I need some assistance with my script.

I have a hash with a name as the key and an integer as the value. Some keys are redundant and for each set of redundants I need to delete all of them except for the one with the highest value.

How can I do this?


Also, how can I make this hash accessible outside of the while loop I had it created in? Is there a way to create global variables from local ones?

Thanks in advance!!

Replies are listed 'Best First'.
Re: Hash Question
by toolic (Bishop) on Nov 23, 2009 at 01:47 UTC
    I'm not sure what you mean by 'Some keys are redundant', but here is one way to delete all hash elements except for the key whose value is the maximum:
    use strict; use warnings; use List::Util qw(max); use Data::Dumper; my %h = (a=>2, b=>8, c=>7); my $max = max(values %h); for (keys %h) { delete $h{$_} unless $h{$_} == $max } print Dumper(\%h); __END__ $VAR1 = { 'b' => 8 };
    If this is not what you are looking for, post some of your hash contents and some of your code.
      It seems to be what I'm looking for but I couldn't get it to work.

      This is what I have

      while (my $seq = <$IN>) { if ($seq =~ /^>(\w+).+\((\d+)\s+aa\)$/) { %sequence = ( $1 => $2, ); my $max = max(values %sequence); for (keys %sequence) { delete $sequence{$_} unless $sequence{$_} == $max } print Dumper(\%sequence); while ( my ($key, $value) = each(%sequence) ) { print $OUT "$key $value\n"; } } }


      Here is an example from the hash
      CIMG_00046 => 399 CIMG_00047 => 865 CIMG_00048 => 330 CIMG_00048 => 506 CIMG_00053 => 167 CIMG_00063 => 468


      I want to delete "CIMG_00048 => 330" because it is less than CIMG_00048 => 506.

      So I would only have
      CIMG_00046 => 399 CIMG_00047 => 865 CIMG_00048 => 506 CIMG_00053 => 167 CIMG_00063 => 468
        use strict; use warnings; my %sequence; while (my $seq = <DATA>) { if ($seq =~ /^(\w+)\s*=>\s*(\d+)/) { $sequence{$1} = $2 unless defined $sequence{$1} && $sequence{$1} > + $2; } } for my $key (sort keys %sequence) { print "$key $sequence{$key}\n"; } __DATA__ CIMG_00046 => 399 CIMG_00047 => 865 CIMG_00048 => 330 CIMG_00048 => 506 CIMG_00053 => 167 CIMG_00063 => 468 CIMG_00063 => 467 CIMG_00063 => 466 CIMG_00063 => 469 CIMG_00063 => 460 __END__ CIMG_00046 399 CIMG_00047 865 CIMG_00048 506 CIMG_00053 167 CIMG_00063 469

        Hashes cannot have multiple same-named keys at the same level.

        perl -le ' use Data::Dump qw/dump/; %hash = { a => 3, a => 2, a => 1, b => 6, }; print dump \%hash; ' { a => 1, b => 6 }
Re: Hash Question
by ikegami (Patriarch) on Nov 23, 2009 at 01:53 UTC
    Another way:
    use List::Util qw( max ); my %h = (a=>2, b=>8, c=>7); my $max = max values %h; my %key_for_val = reverse %h; %h = ( $key_for_val{$max} => $max );
Re: Hash Question
by misterwhipple (Monk) on Nov 23, 2009 at 01:59 UTC
    Also, how can I make this hash accessible outside of the while loop I had it created in? Is there a way to create global variables from local ones?

    At a guess, you're looking for something like this: declare the hash variable outside your loop.

    my %hash; while (<>) { $hash{$_}++; } # %hash is accessible outside the while() loop print join ', ' keys %hash;

    If that's not what you're looking for, post a short excerpt of your code so we can see what you mean.

    --
    Any sufficiently interesting Perl project will depend upon at least one module that doesn't run on Windows.

      I posted my code above but I didn't include that I did declare the hash before the while loop yet I can't see it after the while loop
Re: Hash Question
by tford (Beadle) on Nov 23, 2009 at 01:57 UTC
    use strict; use warnings; + my @names = ('some','keys','are','redundant','keys'); my @values = (1, 3, 4, 5, 2); my %h = (); # so that the hash will be accessible outside the while loop + my $i = 0; while( $i < 5 ) { # a foreach loop would've been better + my $name = $names[$i]; my $value = $values[$i]; unless( defined $h{$name} and $h{$name} > $value ) { # either this is new key-value pair, + # or the value is bigger than what the hash + # has currently + $h{$name} = $value; } $i = $i + 1; } # now print the results + foreach my $key (keys %h) { # keys are in random order print "$key,$h{$key}\n"; }

    This little program produces the following output:

    keys,3 redundant,5 are,4 some,1
Re: Hash Question
by GrandFather (Saint) on Nov 23, 2009 at 03:30 UTC

    Consider:

    use strict; use warnings; my %hash; while (defined (my $line = <DATA>)) { chomp $line; my ($key, $value) = split '', $line, 2; next if ! defined $value; $hash{$key} = $value if ! exists $hash{$key} || $hash{$key} < $val +ue; } print "$_: $hash{$_}\n" for sort keys %hash; __DATA__ a 1 b 3 a 6 c 4 a 2

    Prints:

    a: 6 b: 3 c: 4

    True laziness is hard work