http://qs1969.pair.com?node_id=777841

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

I know that it this is not hard to write by myself, but in order to avoid reinventing the wheel: Does someone know a CPAN utility module, which has a function to create a subset of a hash, so that the newly created hash contains only those keys of the original hash which match a certain regexp? Kind of

my %new_hash=(); grep { $_ =~ $my_regexp && $new_hash{$_}=$original_hash($_) } keys %original_hash;

-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: grep for hash?
by Corion (Patriarch) on Jul 07, 2009 at 11:48 UTC

    Why use grep if you're not returning a list?

    I use grep to create the list of keys and then use map to conver the keys into key/value pairs, which I then assign to the new hash:

    my %new_hash = map { $_ => $original_hash{$_} } grep {/foo/} keys %ori +ginal_hash;
Re: grep for hash?
by jwkrahn (Abbot) on Jul 07, 2009 at 11:48 UTC
    my %new_hash = map /$my_regexp/ ? ( $_ => $original_hash{ $_ } ) : (), + keys %original_hash;
Re: grep for hash?
by Fletch (Bishop) on Jul 07, 2009 at 12:49 UTC

    TMTOWTDI . . .

    use YAML::Syck qw( Dump ); my %orig = qw( a 1 b 2 c 3 d 4 e 5 f 6 ); my %new; { my @keys; @new{ @keys } = @orig{ @keys = grep /[adf]/, keys %orig }; } print Dump( { orig => \%orig, new => \%new } ), "\n"; exit 0; __END__ --- new:   a: 1   d: 4   f: 6 orig:   a: 1   b: 2   c: 3   d: 4   e: 5   f: 6

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      That needlessly makes operand evaluation order relevant. Change
      my @keys; @new{ @keys } = @orig{ @keys = grep /[adf]/, keys %orig };
      to
      my @keys = grep /[adf]/, keys %orig; @new{ @keys } = @orig{ @keys };
      and you have simpler, clearer code.

      I was surprised that this not only works, but also does not complain if strictures are added to the code.

      The code looks ingenious, but I wonder about two aspects: First, you are accessing a list one time as a hash via %NAME, and then the same list as an array, via @NAME. Is this considered accepted practice? Honestly, I hadn't expected it to work with lexically scoped variables, and I'm not sure whether this trick (if I can call it like this) will remain with later Perl versions.

      A more serious problem is the line
      @new{ @keys } = @orig{ @keys = grep /[adf]/, keys %orig };
      where you rely on the fact that @keys gets its value first (from the grep) before it is used to form a slice for @new. I think this is undefined behaviour, isn't it?
      -- 
      Ronald Fischer <ynnor@mm.st>

        All of the references are to the hashes %new and %old; the leading @ and curlies is just the way you name slices. The only array involved in the whole thing is @keys.

        As for relying on the ordering, I think it's pretty safe to count that the rvalue will be computed and available before the lvalue is populated with it.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

Re: grep for hash?
by Anonymous Monk on Jul 07, 2009 at 11:45 UTC
    Not me my %new =  map { $_ => $orig{$_}; } grep {/$my_regexp/ } keys %orig;
Re: grep for hash?
by poolpi (Hermit) on Jul 07, 2009 at 12:38 UTC

    Just with map:

    use strict; use warnings; use Data::Dumper; my $orig = { 'foo' => 2, 'bar' => 4, 'boo' => 1, 'zar' => 5 }; my $dest; map { /oo/ and $dest->{$_} = $orig->{$_} } keys %$orig; print Dumper $dest; __END__ Output: $VAR1 = { 'boo' => 1, 'foo' => 2 };


    hth,
    PooLpi

    'Ebry haffa hoe hab im tik a bush'. Jamaican proverb

      Map in void context? Bleh. (See map versus for)

      The cake is a lie.
      The cake is a lie.
      The cake is a lie.

      Should be :D  /oo/ and $dest->{$_} = $orig->{$_} for keys %$orig;

        Written like it's said, "Copy each selected key":

        $dest->{$_} = $orig->{$_} for grep /oo/, keys %$orig;

        There's no need to twist one's mind to process the parent's expression.

Re: grep for hash?
by LanX (Saint) on Jul 07, 2009 at 14:54 UTC
    This is such a frequently reinvented wheel! 8)

    IMHO something like hgrep() or hmap() should be somewhere in the core-moduls... with a code-prototype it's easy to accomplish.

    Anyway TIMTOWTDI:

    Without explicit grep

    %hash_2= map { /regex/ ? ( $_ , $hash_1{$_} ) : () } keys %hash_1;

    Hashslice:

    my @keys = grep { /regex/ } keys %hash_1; @hash_2{@keys} = @hash_1{@keys};

    http://www.perl-community.de/bat/poard/message/120873

    Cheers Rolf