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

Hi fellow monks!
I would like to be able to parse a hash table for only completely uppercase variables.. I think this can be done but I'm not sure how..
#!/bin/perl -w use strict; $runset{FlowShortName}="foo"; $runset{abc}="test"; $runset{TEST}="I"; $runset{ABC}="am"; $runset{BAR}="a monk in training"; #some smidgen of code to delete non upper case variables foreach my $key (keys %runset){ print "$key $runset{$key}"; }
Would print..
"I am a monk in training"

Thanks much

Replies are listed 'Best First'.
Re: Parsing a hash table for only variables that are uppercase.
by rob_au (Abbot) on Mar 24, 2002 at 00:18 UTC
    As always there are many ways to do this - the first which came to my mind was to use grep. eg.

    foreach my $key ( grep { /^[A-Z]+$/ } keys %runset ) { print $key, " ", $runset{$key}, "\n"; }

    ... But if you really wanted to delete the non-uppercase hash elements, then expanding on this usage of grep ...

    delete $runset{$_} for ( grep { !/^[A-Z]+$/ } keys %runset );

     

    perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print'

      Minor regex nit:

      Rather than checking the entire key for having uppercase letters and then negating the rest, you could check for the presence of any non-uppercase letter:

      delete $runset{$_} for ( grep { /[^A-Z]/ } keys %runset );

      Of course, this will also allow the null hash key as well, so you might want to extend the grep to grep { $_ && /[^A-Z]/ }, and by that point, you may have sacrificed the savings on the regex.

      - Richie

Re: Parsing a hash table for only variables that are uppercase.
by Albannach (Monsignor) on Mar 24, 2002 at 00:28 UTC
    Well since you say delete you could use something like
    for (keys %runset) { unless($_ eq uc($_)) { delete $runset{$_}; } }
    before your printing loop.

    However you have another problem: a hash is un-ordered, so you won't get the sentence you expect unless you are very lucky - you need some way to specify the order in which the values of the uppercase keys are printed. The example you provide doesn't work for a simple sort of the uppercase keys so you'll have to think of something else.

    --
    I'd like to be able to assign to an luser

      While it goes beyond the scope of the problem, a hash can be made to be ordered by using the CPAN module Tie::IxHash.

      Link to Tie::IxHash documentation
      Link to Tie::IxHash tarball

      Usage:

      #!/usr/bin/perl -w
      
      use strict;
      use Tie::IxHash;
      
      tie my %ordered_hash, Tie::IxHash;
      
      %ordered_hash = (a => 1, b => 2, c => 3);
      
      foreach my $key ( keys %ordered_hash ) {
          print "$key\t$ordered_hash{$key}\n";
      }
      
      # prints:
      # a     1
      # b     2
      # c     3
      
      exit 0;
      
      

      Cheers,
      Richard

Re: Parsing a hash table for only variables that are uppercase.
by seattlejohn (Deacon) on Mar 24, 2002 at 00:33 UTC
    A previous reply gives some methods for deleting keys are that aren't upper case, but there's one other thing that you should be aware of: hash keys are not stored (or returned by keys) in any particular order. So your code might print "I am a monk in training", or it might print "a monk in training am I", which sounds like something Yoda might utter.

    Actually, as long as your print statement includes $key, your output is also not going to be formatted quite as you expect: "TEST IABC amBAR a monk in training" is one possibility. (Note the lack of spaces, too.) What you really want inside the loop is probably something like this:
    print $runset{$key}, " "

    Or, even more Perlish, get rid of the enclosing loop and just use this:
    print join " ", values %runset;

    This latter version also eliminates the trailing space, because join only inserts delimiters between list elements.

Re: Parsing a hash table for only variables that are uppercase.
by Rhodium (Scribe) on Mar 24, 2002 at 01:15 UTC
    Thanks for the replies. I really do appreciate it!
    Now I want to expand this a pinch, now that some of you think my deleting of hash keys is not needed.
    I have two hash tables (%runset and %description) and I want to do the following. All of the keys in %runset have precendence over %description. BUT if there are "new" ones in %description, I don't want to forget those either.. So here was my thoughts on how to do this..
    1. Only valid runset variables in %runset are Uppercase(This is why deleting them is easier..)
    2. Comparing the two hashes grab %runset first and as you print them delete them from both hashes (if they exist in %description - which they might not).
    3. Now any remaining hashes in %description print them as well.
    Am I crazy?
    If you want to try your hand at this code I would really be indebted to you all. Thanks much!!
      This is actually much easier than you might expect - Essentially you are looking at merging two hashes with the key-values of %runset having precedence of those in %description. This can be done as follows:

      delete $runset{$_} for ( grep { !/^[A-Z]+$/ } keys %runset ); %merged_hash = ( %description, %runset );

      This code will delete the non-uppercase key-value pairs in the %runset and then merge the two hashes - Any values in %description with an identical key-name in %runset will be replaced with the values in the latter (based on left-to-right execution).

       

      perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print'

        Hi Rob,

        Thanks for sharing your knowledge. Even in your .sig?

        I tried pasting your .sig:

        $ $ perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print' In string, @cowsnet now must be written as \@cowsnet at -e line 1, nea +r "rob@cowsnet" Execution of -e aborted due to compilation errors. $ perl -e 's&&rob\@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print' rob@cowsnet_au$

        Am I missing something?

        HTH, Danny

        Update:

        $ perl -v This is perl, v5.6.0 built for i586-linux Copyright 1987-2000, Larry Wall . . .
Re: Parsing a hash table for only variables that are uppercase.
by pizza_milkshake (Monk) on Mar 24, 2002 at 19:43 UTC
    Not sure if this is the *best* way, but I'm lazy and it works for me
    
    
    #!/usr/bin/perl use strict; my %h = qw/A a B b c c/; my %h2; $h2{$_} = $h{$_} for grep {/^[^a-z]$/} keys %h;
    perl -MLWP::Simple -e'$_="104116116112058047047112097114115101101114114111114046099111109047112"; $s .= chr for/(...)/g;getprint $s' |more