http://qs321.pair.com?node_id=11117569

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

Got this to filter out undefined values indirectly from a hash slice:

my %args = (profile => 'foo'); my %ta = %args{'profile', 'user', 'password'}; %ta = map { $_ => $args{$_} } grep { $args{$_ } } keys %ta;

It works well enough. But in trying to make it a little more concise:

my %args = (profile => 'foo'); %ta = map { $_ => $args{$_} } grep { $args{$_ } } keys %args{'profil +e', 'user', 'password'};

you get an error: Experimental keys on scalar is now forbidden. Is there a good, more perlish way of pulling this off?

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

Replies are listed 'Best First'.
Re: Consise way to filter out keys with undef values from a hash slice?
by choroba (Cardinal) on Jun 01, 2020 at 20:21 UTC
    What about grepping the keys even before the undef values are created?
    #!/usr/bin/perl use warnings; use strict; use Data::Dumper; my %args = (profile => 'foo'); my %ta = %args{ grep exists $args{$_}, qw( profile user password ) }; print Dumper \%ta;
    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Consise way to filter out keys with undef values from a hash slice?
by davido (Cardinal) on Jun 02, 2020 at 14:59 UTC

    So you want to filter-in hash elements that have a defined value:

    my %args = (profile => 'foo'); my %ta = map {defined($args{$_} ? ($_ => $args{$_}) : ()} qw(profile u +ser password);

    If a map iteration returns an empty list, nothing gets added to the recipient's list. I don't usually like making map behave like grep, but in this case it seems a reasonable fit.

    grep behavior can be provided by map by adding a Boolean test and a conditional return value.

    my @array = grep {COND} LIST; my @array = map {COND ? $_ : ()} LIST

    Dave

Re: Consise way to filter out keys with undef values from a hash slice?
by LanX (Saint) on Jun 01, 2020 at 21:16 UTC
    This

    >  grep { $args{$_ } } keys %ta;

    Will also delete all false values like 0 or "" . You rather want to test on definedness and not truth

    This keys on a slice

    >   keys %args{'profile', 'user', 'password'};

    Is over complicated and doesn't make sense.

    You obviously just want the list of keys directly

    'profile', 'user', 'password'

    But I concur with choroba that using exists right from the beginning is the cleanest way, since undef could be a legal value.

    edit

    There is - unfortunately - no "slice when exists" operator in Perl.

    Otherwise (ab)using hashes for set operations like intersections would be trivial.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      On a side note,

      I was surprised that the following is even legal syntax. I only knew slicing lists with @

      >  %args{'profile', 'user', 'password'};

      Turns out it was introduced with 5.20 and returns a list of key/value pairs.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

      Otherwise (ab)using hashes for set operations like intersections would be trivial.

      Though off-topic here: Is there a common idiom for an intersection of two hash-sets? The first thing that comes to my mind is something like:

      %a_b = map {exists $b{$_} ? ($_ => $b{$_}) : ()} keys %a;

      Greetings,
      -jo

      $gryYup$d0ylprbpriprrYpkJl2xyl~rzg??P~5lp2hyl0p$
Re: Consise way to filter out keys with undef values from a hash slice?
by kcott (Archbishop) on Jun 02, 2020 at 09:13 UTC

    G'day nysus,

    "Is there a good, more perlish way of pulling this off?"

    In the absence of any information about wanting to capture the deleted keys, I'd probably just do this:

    delete @ta{grep !defined $ta{$_}, keys %ta};

    I'm not sure if the way you've constructed %ta is really what you intended; however, with Perl 5.30.0 I get no errors or warnings:

    $ perl -Mstrict -Mwarnings -E ' my %args = (profile => "foo"); my %ta = %args{qw{profile user password}}; say "BEFORE:"; say for keys %ta; delete @ta{grep !defined $ta{$_}, keys %ta}; say "AFTER:"; say for keys %ta; ' BEFORE: profile password user AFTER: profile

    — Ken

      > with Perl 5.30.0 I get no errors or warnings

      5.20.0 Delta: New slice syntax.

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Consise way to filter out keys with undef values from a hash slice?
by AnomalousMonk (Archbishop) on Jun 01, 2020 at 21:32 UTC
    ... filter out undefined values ... [emphasis added]

    My take is that keys with undefined values (either because non-existent or undef) are to be filtered out. One way:

    c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "my %args = ('profile' => 'foo', 'xyzzy' => undef, 'status' => 0); my %ta = map { defined $args{$_} ? ($_ => $args{$_}) : () } qw(profile password xyzzy status) ; dd 'filtered', \%ta; dd 'original', \%args; " ("filtered", { profile => "foo", status => 0 }) ("original", { profile => "foo", status => 0, xyzzy => undef })


    Give a man a fish:  <%-{-{-{-<

      Here a concise way for cleaning up afterwards:

       $h{$_} // delete $h{$_} for keys %h

      Unfortunately the even shorter approach doesn't work with deleting aliases

      $_ // delete $_ for values %h

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

Re: Consise way to filter out keys with undef values from a hash slice?
by tybalt89 (Monsignor) on Jun 01, 2020 at 22:31 UTC
    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11117569 use warnings; my %args = (profile => 'foo'); my %ta = %args{ grep defined $args{$_}, 'profile', 'user', 'password' +}; use Data::Dump 'dd'; dd \%ta;