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

I have yet to play around much with map and grep. For practice, I wanted to see how you might best create a subset of a hash. The following code generates a hash containing entries whose keys begin with 't' and is a subset of a larger hash whose keys are random. Here's what I came up with:
my %hash = (tom => 23, larry=> 't0', tina=> 23); my @filteredhash = grep{/^t/} keys %hash; my %newhash = map{$_, $hash{$_}} @filteredhash;
Surely there's a more efficient way than my hackish two-step code. Perhaps nest the grep inside the map:
my %newhash = map{$_, $hash{$_}} grep{/^t/} keys %hash;
That seems a little weird too. Help me out and I'll buy you a beer at the 19th hole.

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop";
$nysus = $PM . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Miniature golf question
by suaveant (Parson) on Jul 16, 2001 at 22:52 UTC
    Trick I learned in Damian's class...
    map {/^t/?($_,$hash{$_}):()} keys %hash;

    Update Golfed I guess it would be...

    %h=map{/^t/?($_,$h{$_}):()}keys%h;

                    - Ant

      Very cool. I felt in bones there had to be a way to do it just using map. Thanks.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop";
      $nysus = $PM . $MCF;
      Click here if you love Perl Monks

Re: Miniature golf question
by MeowChow (Vicar) on Jul 16, 2001 at 22:59 UTC
    Another WTDI:
    my %newhash = %hash; delete @newhash{grep !/^t/, keys %newhash};
       MeowChow                                   
                   s aamecha.s a..a\u$&owag.print
      Golfed this is way shorter than mine... though my technique could be used in other places, this one is a much lower par for this hole :)

      Update heres a sneaky golf trick, tho...

      delete@h{grep!/^t/,%h};
      You don't need the keys... sure it will look at the values, but it will only try to delete the values as keys if they start with a t, and they should be deleted anyway, and if it doesn't have a key that matches it, it ignores it... save 4 strokes... woohoo

                      - Ant

Re: Miniature golf question
by chipmunk (Parson) on Jul 16, 2001 at 23:27 UTC
    For efficiency, you generally want to avoid map expressions that return more than one element for each element of the original list, at least in current versions of Perl. As I understand it, map preallocates a list to hold the results, but when you return several elements for each individual element, there's a lot of overhead to accomodate the extra elements.

    Here's a way to do the transfer without using map:

    my @keys = grep /^t/, keys %hash; my %newhash; @newhash{@keys} = @hash{@keys};
    On the other hand, I did a quick benchmark, which ended up showing little difference in efficiency between the solutions:
      Your efficiency note is only true until Perl 5.6.1.

      The problem is that Perl builds the return list in place. In old versions it would only allocate the minimum necessary space, moving data every time. The moving of data was a O(n2) bottleneck. Starting in 5.6.1 it will allocate the minimum of the minimum necessary, and the length of the data stack. This results in a power of 2 reallocation strategy that winds up being O(n) in terms of moving data.

      To experience the difference that this makes, try running this statement in both a new and old version of Perl:

      my @big_list = map {($_, $_)} 1..50_000;
      If it takes a while, you aren't on 5.6.1 yet. :-)
        Tilly, I started to read "Structure and Interpretations of Computer Programs" and it was right around the stuff you bring up above that I started to get lost. Too much for a guy who blew off H.S. more than he should have. So until I brush up on my math skills, do you know of a lighter introduction to the kind of concepts you mention above? Thanks, bro.

        $PM = "Perl Monk's";
        $MCF = "Most Clueless Friar Abbot Bishop";
        $nysus = $PM . $MCF;
        Click here if you love Perl Monks

      I think he was looking for golf solutions, since his title is miniature golf... so its more the terseness of the code, throwing efficiency to the wind...
      Hehe, I certainly wouldn't want to use the grep without the keys call anywhere else :)

                      - Ant