in reply to Re: Selecting a Specific Keys in Relation to a List
in thread Selecting a Specific Keys in Relation to a List

While your algorithm works (mostly), it trades a small memory gain for a lot of extra CPU work, especially as @do_not_want grows.

For every key in %hash, the code must loop through every member of @do_not_want. Grep does not short-circuit, so even after you've found 'ytd', you are still going to check 'mtd' and 'wtd'.

The other problem with this code is that if $hash{ytd_total} = 1000; is a desired report value, your grep will match and incorrectly exclude that value.

As long as pure textual equivalence is the matching criteria, using a hash for the lookup is going to be faster and make a tiny impact on memory.

If you need to do some kind of complex matching, then there is no way (that I know of) to avoid nested loops. But you can use a short circuited loop to avoid checking for any matches after the first. List::MoreUtils any comes to mind as an excellent way to handle this situation.

Here's a version that uses a hash

my %hash = ( ytd => 1.5, mtd => 2.0, wtd => 2.5, Jumbo_Tron => "United Center", Meat_Pie => "Gross", "Word" => "Association", ); my %do_not_want; @do_not_want{ qw(ytd mtd wtd) } = (); foreach my $k (keys %hash) { next if exists $do_not_want{$k}; print "$k => $hash{$k}\n"; }

Here's a version that matches hash keys against a regex:

use List::MoreUtils qw(any); my %hash = ( ytd => 1.5, mtd => 2.0, wtd => 2.5, Jumbo_Tron => "United Center", Meat_Pie => "Gross", "Word" => "Association", ); my @do_not_want = ( qr/ytd/, qr/mtd/, qr/wtd/ ); foreach my $k (keys %hash) { next if any { $k =~ $_ } @do_not_want ; print "$k => $hash{$k}\n"; }

Warning, all this code is untested. I modified it in a browser edit box.


TGI says moo

Replies are listed 'Best First'.
Re^3: Selecting a Specific Keys in Relation to a List
by tuxz0r (Pilgrim) on Nov 02, 2007 at 03:38 UTC
    I agree about using exists on a hash of "unwanted" keys instead of the array. Good point. I didn't go that route due to the small number of keys (3) in the example. For larger sets of keys to check against, a grep on a array would definitely not be the way to go.

    And, the matching in the regex is easily fixed by using /^$k$/ instead, which would allow the possible exceptions like ytd_total, etc.

    Hadn't thought about List::MoreUtils, either.

    ---
    echo S 1 [ Y V U | perl -ane 'print reverse map { $_ = chr(ord($_)-1) } @F;'

      For a small number of keys, the nested loop isn't so bad. I've been burned enough times by changing requirements that if I have two algorithms of similar complexity to code and cost to run, I'll almost always favor the one that scales better, even if the ability to scale isn't strictly necessary.

      The matching regex should probably include a quotemeta as well as the start and stop anchors: /^\Q$k\E$/. So key names like day* are safe. This SHOULD cover all the possible mixups. I think it does. I'm pretty sure...

      All the above struggling with subtle bugs demonstrates something important. If you are testing for string equivalence, use the string equivalence operator, and not a regex.

      So, if I decided to implement the code with a call to any I'd use:

      next if any { $_ eq $k } @do_not_want;


      TGI says moo