Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Mapping array over a hash

by agoth (Chaplain)
on Jul 20, 2001 at 17:52 UTC ( [id://98404]=perlquestion: print w/replies, xml ) Need Help??

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

Hi,
I'm trying to convert a loop into a map construct.
I am trying to end up with a hash populated with only those name - value pairs that correspond to the values in an array.
I can do the loop, and am almost there with the map, but getting "use of unitialised value in hash assignment" and dont know why map is not dropping the value?
The loop at the top is what I've got, the map is almost what I want, thoughts?
my %hash1 = ('one' => 'un', 'two' => 'deux', 'three' => 'trois'); # f +ixed my @ary = ('one', 'two', 'four'); # v +ariable #----------------------------------------- for (keys %hash1) { my $loc = $_; my $mat = 0; for (@ary) { $mat = 1 if ($_ eq $loc); } delete $hash1{$loc} unless $mat; } for (keys %hash1) { print "**$_** => **$hash1{$_}**\n"; } #----------------------------------------- my %hash2 = map { $_ => $hash1{$_} if (defined $hash1{$_}) } @ary; for (keys %hash2) { print "**$_** => **$hash2{$_}**\n"; } #-----------------------------------------

Replies are listed 'Best First'.
Re: Mapping array over a hash
by Masem (Monsignor) on Jul 20, 2001 at 17:56 UTC
    I believe that you're running into operator precedence problems between the 'if' modifier and the => operator. Instead, try a grep in there as well:
    my %hash2 = map { $_ => $hash1{$_} } grep { defined $hash1{$_} } @ary;
    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
      It came to me about a minute after you'd posted, after about an hour of faffing, thanks anyway!!!!
      grep is the answer!
Re (tilly) 1: Mapping array over a hash
by tilly (Archbishop) on Jul 20, 2001 at 18:22 UTC
    map works like a function call. It returns the last thing executed in the block. And if that was the failure of a defined test, then it is a false value. The statement you wanted is:
    my %hash2 = map {defined($hash1{$_}) ? ($_ => $hash1{$_}) : ()} @ary;
    But note that I would avoid this with large arrays unless you are on 5.6.1 or better.
Re: Mapping array over a hash
by arturo (Vicar) on Jul 20, 2001 at 18:03 UTC

    For a quick, but possibly wrong solution, you could use a hash slice:

    my %newhash; @newhash{@array} = @oldhash{@array};

    A pitfall here is that if there's a value in the array that doesn't correspond to a key in the original hash, you'll autovivify an entry.

    If you don't have such control over your data, try:

    my %new = map { $_=> $old{$_} } grep { defined $old{$_} } @array;

    The grep gets all the entries in the hash that (a) have a key corresponding to an entry in the array and (b) have defined values. The map turns that list into a hash.

    Change that defined to exists if you don't mind getting back keys associated with undefined values.

    HTH!

    perl -e 'print "How sweet does a rose smell? "; chomp ($n = <STDIN>); +$rose = "smells sweet to degree $n"; *other_name = *rose; print "$oth +er_name\n"'
      Autovivification does not occur here; it only happens when the non-existent thing in question is an lvalue:
      %hash = qw( a 1 b 2 c 3 ); @array = (1,2,4,8); $x = $hash{z}; # no auto-viv $x = $array[10]; # no auto-viv 1 for $hash{y}; # auto-viv 1 for $array[7]; # auto-viv
      Bonus: why are those bottom two treating the value in question as an lvalue?

      _____________________________________________________
      Jeff japhy Pinyan: Perl, regex, and perl hacker.
      s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

        looks like because they're being evaluated in list context - but I don't even know what vivification is, let alone autovivification

        I'm off to look it up.

        update: hell, I can't even spell it!

        update2: found a useful article here - turns out I do most of this already - I just didn't know what it was called!

        update3: for supersearch: "what is autovivification?" and for muppets like me in the supersearch "what is autovivication?"

        "Argument is futile - you will be ignorralated!"

Re: Mapping array over a hash
by dragonchild (Archbishop) on Jul 20, 2001 at 17:56 UTC
    You're trying to be too complicated. Instead of taking a hash and removing what shouldn't be there, why don't you just create a new hash by adding what should be there. Something along the lines of:

    my %hash2; for (@ary) { $hash2{$_} = $hash1{$_} if exists $hash1{$_}; } undef %hash1;
      UPDATED For the reason that the array is coming in from user input (Getopt::Long), so I don't want to add unknown options to my final hash.
      I want to pare down the entries in the hash to the mimimum amount of keys corresponding to only known parameters that i have been given.
      cheers though, Sorry the first draft of this reply was gibberish
      my %opts; my %locs = map { $_ => 1 } ('do', 'ny', 'kw'); my %sect = ('uid' => 1, 'email' => 'a', 'username' => 'a'); my $USAGE = qq( USAGE: perl launch.pl (-l=<location>)+ (-d=<data>)+ Where -l=<location> = [do|kw|ny] = one or more times -d=<data_type> = [uid|email|username] = one or more times ); die $USAGE unless (scalar(@ARGV) >= 2); GetOptions(\%opts, "l=s@", "d=s@"); die $USAGE unless (defined $opts{'l'}); die $USAGE unless (defined $opts{'d'}); %locs = map { $_ => $locs{$_}} grep defined $locs{$_}, @{ $opts{'l'} +}; # strips out keys from hash if %sect = map { $_ => $sect{$_}} grep defined $sect{$_}, @{ $opts{'d'} +}; # not requested by user die $USAGE unless scalar keys %locs > 0; die $USAGE unless scalar keys %sect > 0;
        Umm... you won't be. The only keys in %hash1 that are ever checked are values that exist in @ary. I'm assuming that @ary is the list of allowable parameters. That would mean that any values in %hash1 that didn't exist in @ary would never make it into %hash2. And, likewise, any values in @ary that don't exist in %hash1 don't make it into %hash2.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://98404]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (6)
As of 2024-04-23 07:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found