Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Slice a hash to get a subset hash.

by chrestomanci (Priest)
on Feb 24, 2011 at 11:15 UTC ( [id://889961]=perlquestion: print w/replies, xml ) Need Help??

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

Forgive the easy question, and please tell me if there is no answer. This is not homework by the way.

I am looking for a neat way to slice a hash to get another hash. I know that hashes can be sliced to get arrays, and that arrays can be sliced to get other arrays. What I am looking for is a neat way to get a hash of a subset of fields from a bigger hash.

Eg: To slice an array to get a smaller array is easy:

my @numbers = ( 0..10 ); my @prime_numbers = @numbers[2,3,5,7];

Also you can slice a has to get an array:

my %london = ( 'Area' => 1572, # Sq Km 'Population' => 7_556_900, 'Elevation' => 24, # m 'Time_Zone' => 'GMT', 'Mayor' => 'Boris Johnson', 'Long' => '0.1275°W', 'Lat' => '51.50722°N', ); my @lat_long = @london{'Lat','Long'};

What I am trying to do is to find a neat way to get a subset hash of just the fields I am interested in. It feels like there ought to be a way in the language, but I can't find it, and my google searches are just finding the standard hash to array form of slicing. What I would like to be able to do is something like this:

my @location_fields = qw( Lat Long Elevation ); # Using the %london hash defined above my %london_location = %london{ @location_fields };

Except it does not work. The best I could come up with is:

my %london_location = map {$_=>$london{$_}} @location_fields;

Is there a neater way?

Replies are listed 'Best First'.
Re: Slice a hash to get a subset hash.
by moritz (Cardinal) on Feb 24, 2011 at 11:21 UTC
    my %newhash; @newhash{@selection} = @oldhash{@selection};

    In your example:

    my %london_location; @london_location{@location_fields} = @london{ @location_fields };

    Which is concise, but still repeats both the new hash and the selection.

    FWIW in Perl 6 the following is supposed to work (but doesn't yet in Rakudo):

    my %newhash = %oldhash{@selection}:pairs

    (Update: while re-reading the specification it seem it must be :p instead of :pairs).

      Thank you.

      I asked the question because It just felt as if there ought to be something in the language to slice hashes. It is interesting that it is a planned feature for Perl 6. I guess that someone agrees that the feature should be there.

Re: Slice a hash to get a subset hash.
by ELISHEVA (Prior) on Feb 24, 2011 at 12:03 UTC

    What I usually do is something like this (your final solution in the OP):

    my %location = map { $_ => $london{$_} } qw(Long Lat Elevation);

    However, if that isn't clean enough for you, you could use prototypes to create a bit of syntactic sugar:

    sub extract(\%@) { my $h = shift; map { $_ => $h->{$_} } @_; } # usage my %location = extract %london, qw(Long Lat Elevation); my %stats = extract %london, 'Population', 'Area'

      Thank you, that is a nice bit of syntactic sugar. I would add it to my toolbox, but I suspect that if I did I would be for ever explaining it to the other perl programers at $work.

      I think I will stick with the map method that I put in the original post. It is good enough.

Re: Slice a hash to get a subset hash.
by BrowserUk (Patriarch) on Feb 24, 2011 at 11:24 UTC

    Neater? Eye of the beholder...but it is pretty self explanatory.

    my @location_fields = qw( Lat Long Elevation );; my %loc; @loc{ @location_fields } = @london{ @location_fields };; pp \%loc;; { Elevation => 24, Lat => "51\xF830′26″N", Long => "0\xF87′39″W", }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Slice a hash to get a subset hash.
by ikegami (Patriarch) on Feb 24, 2011 at 16:09 UTC

    What I would like to be able to do is something like this: my %london_location = %london{ @location_fields };

    I believe that's in the works.

Re: Slice a hash to get a subset hash.
by techjohnny (Novice) on Feb 25, 2011 at 04:28 UTC
    I'm a newbie and learning Perl by the minute. I'm not even sure if this is what you're looking for, but here is what I've come up with: my @getme = qw(Area Mayor Long Lat); %newlondon; @newlondon{$_}=$london{$_} for @getme; Please let me know if this helps.

      Thank you for your reply. I hope it will be the first of many in this monestery. Welcome.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (5)
As of 2024-04-16 19:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found