Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

[Answered!] Taking a subset of a hash

by Narveson (Chaplain)
on Oct 30, 2010 at 13:02 UTC ( [id://868463]=perlquestion: print w/replies, xml ) Need Help??

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

Update

I like both the replies that just came in. Thank you both!

I am given a configuration hash reference and three keys of interest. I have to return a hash reference that contains at most the three keys of interest, but omits them if their configured value is false, undefined, or nonexistent. So the simple hash-slicing solution

my %small_hash; @small_hash{@interesting_keys} = @$config{@interesting_keys}; return \%small_hash;

is not acceptable, because keys of interest with false or undefined values are retained, and I want them not to exist.

I have written the following pedestrian solution:

# $config is given sub get_file_watcher_ref { my %setting_for; my ( $watch_interval, $watch_file_min_size, $term_run_time, ); if ( $watch_interval = $config->{watch_interval} ) { $setting_for{watch_interval} = $watch_interval; } if ( $watch_file_min_size = $config->{watch_file_min_size} ) { $setting_for{watch_file_min_size} = $watch_file_min_size; } if ( $term_run_time = $config->{term_run_time} ) { $setting_for{term_run_time} = $term_run_time; } return \%setting_for; }

which (barring typos - I haven't tested it yet) does what I want done. But it's horribly repetitive.

Does anyone know an elegant way to write this?

Update

I must have momentarily forgotten how to loop. Here's what I meant to do.

sub get_file_watcher_ref { my %setting_for; my @interesting_keys = qw( watch_interval watch_file_min_size term_run_time ); for (@interesting_keys) { if ($config->{$_}) $setting_for{$_} = $config->{$_}; } return \%setting_for; }

The loop still feels kind of clumsy. Any suggestions for improvement?

Replies are listed 'Best First'.
Re: Taking a subset of a hash
by choroba (Cardinal) on Oct 30, 2010 at 13:17 UTC
    Like this?
    my %small_hash; my @keys_to_keep = grep $config->{$_},@interesting_keys; @small_hash{@keys_to_keep} = @$config{@keys_to_keep}; return \%small_hash;
Re: Taking a subset of a hash
by BrowserUk (Patriarch) on Oct 30, 2010 at 13:22 UTC
    Does anyone know an elegant way to write this?

    Perhaps?

    my %small_hash; my @retain = grep{ defined $config{ $_ } && $config{ $_ } ## not false } @interesting_keys; @small_hash{ @retain } = @config{ @retain }; return \%small_hash;

    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.

      Sorry if I forgot to mention that $config was a reference.

      Doesn't defined $config{ $_ } && $config{ $_ } have the same truth value as $config{ $_ }?

        Doesn't defined $config{ $_ } && $config{ $_ } have the same truth value as $config{ $_ }?

        Yes. I was attempting to capture your stated requirements:

        but omits them if their configured value is false, undefined, or nonexistent.

        and got it wrong. It should be:

        exists $config{ $_ } && $config{ $_ }

        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: Taking a subset of a hash
by james2vegas (Chaplain) on Oct 30, 2010 at 13:18 UTC
    @small_hash{@interesting_keys} = @$config{@interesting_keys}; delete @small_hash{ grep { ! $config->{$_} } keys %$config };
    should work
Re: Taking a subset of a hash
by roboticus (Chancellor) on Oct 30, 2010 at 13:37 UTC

    Narveson:

    If I understand you correctly:

    use strict; use warnings; use Data::Dumper; my %config = (a=>undef, b=>0, c=>'foo'); my @wanted = qw(a b c d); my %result = get_small_hash(\%config); sub get_small_hash { my $hr = shift; return map { $_ => $$hr{$_} } grep { exists($$hr{$_}) and defined($$hr{$_}) and $$hr{$_} +} @wanted; }

    This gives me:

    roboticus@Boink:~ $ perl return_grep_slice.pl $VAR1 = { 'c' => 'foo' };

    Oh, well, I notice that in the time it took me to write & test this, you've already gotten some responses. But I've already gone this far...

    ...roboticus

Re: [Answered!] Taking a subset of a hash
by ig (Vicar) on Oct 30, 2010 at 21:48 UTC

    I would probably have written something like:

    use strict; use warnings; use Data::Dumper; my %hash = ( a => 1, b => 0, c => undef, e => 1, f => 0, g => undef ); my @interesting_keys = qw( a b c d ); my %small_hash = map { ($hash{$_}) ? ($_ => $hash{$_}) : () } @interesting_keys; print Dumper(\%small_hash);

    but maybe the following is easier to understand...

    my %small_hash = map { $_ => $hash{$_} } grep { $hash{$_} } @interesting_keys;

    Both give the same result:

    $VAR1 = { 'a' => 1 };

    which is what you are wanting, if I understood correctly.

    These are only a little different from some of the other suggestions you have received and I don't suggest they are necessarily any better. Which is best for you depends on what you and the others that might read you code know of Perl. You should choose a solution which you find easy to understand and modify or re-use as your needs evolve.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (2)
As of 2024-04-26 05:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found