Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

do something if hash keys match regex

by anandk (Initiate)
on Jul 20, 2017 at 08:03 UTC ( [id://1195568]=perlquestion: print w/replies, xml ) Need Help??

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

O wise monks, greetings. I'm trying to replace the values of a hash based on the keys matching a regex. Is there a more compact, one liner way of doing it, without using foreach and the braces ? Most ideally I would want something like this:

$hash{/abc_(.*)_ghi/} = boom_$1
That should reassign all the 4 key-value pairs in the hash.

My current code:
use Data::Dumper; my %hash = ( xxx_text10_yyy => 1, xxx_text11_yyy => 1, mmm_text12_nnn => 1, mmm_text13_nnn => 1, abc_text6_ghi => 1, abc_text7_ghi => 1, abc_text8_ghi => 1, abc_text9_ghi => 1 ); print Dumper(\%hash); foreach my $key (keys %hash) { $hash{$key} = "boom_$1" if ($key =~ /abc_(.*)_ghi/); $hash{$key} = "doom_$1" if ($key =~ /mmm_(.*)_nnn/); } print Dumper(\%hash);

Replies are listed 'Best First'.
Re: do something if hash keys match regex
by Eily (Monsignor) on Jul 20, 2017 at 08:33 UTC

    You won't get much better than the foreach solution, which has the advantage of being easy to read. You could use grep to select the keys: $hash{$_} = "boom_$1" for grep /abc_(.*)_ghi/, keys %hash;

    I think the way you generate your data is a bit to complex to be turned into a readable map generation: my %hash = { /abc_(.*)_ghi/ ? ($_ => "boom_$1") : ($_ => 1) } @list_of_keys; wouldn't work so well with the two (or more) separate conditions, or wouldn't be as readable as your current solution.

Re: do something if hash keys match regex
by shmem (Chancellor) on Jul 20, 2017 at 08:44 UTC

    There is no way to select hash keys with a regular expression other than iterating over the keys, be that done with a for/foreach loop, map, or a statement modifier. So, no, you can't do that without using foreach and braces.

    But you could stuff your regular expression components into another hash, to avoid repeating yourself inside the foreach loop

    my %re = ( boom => [ qw( abc ghi) ], doom => [ qw( mmm nnn) ], zapp => [ qw( xxx yyy) ], ); foreach my $key (keys %hash) { $key =~ /$re{$_}->[0]_(.*)_$re{$_}->[1]/ and $hash{$key} = $_.$1 for keys %re; }

    which arguably isn't as readable as your current solution.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: do something if hash keys match regex
by tybalt89 (Monsignor) on Jul 20, 2017 at 11:38 UTC

    Braces? Braces? We don't need no stinkin' braces.

    #!/usr/bin/perl # http://perlmonks.org/?node_id=1195568 use strict; use warnings; use Data::Dumper; my %hash = ( xxx_text10_yyy => 1, xxx_text11_yyy => 1, mmm_text12_nnn => 1, mmm_text13_nnn => 1, abc_text6_ghi => 1, abc_text7_ghi => 1, abc_text8_ghi => 1, abc_text9_ghi => 1 ); print Dumper(\%hash); %hash = ( %hash, map( /abc_(.*)_ghi/ ? ( $_, "boom_$1" ) : /mmm_(.*)_nnn/ ? ( $_, "doom_$1" ) : (), keys %hash ) ); print Dumper(\%hash);
Re: do something if hash keys match regex
by kcott (Archbishop) on Jul 21, 2017 at 07:03 UTC

    G'day anandk,

    Welcome to the Monastery.

    "$hash{/abc_(.*)_ghi/} = boom_$1"

    There's all sorts of problems with that; let's step through them.

    $ perl -wE 'use Data::Dump; my %h; dd \%h; $_ = "a_b_c"; $h{/a_(.*)_c/ +} = boom_$1; dd \%h' {} Can't call method "boom_" on an undefined value at -e line 1.

    So the RHS (right-hand side) would need to be quoted.

    $ perl -wE 'use Data::Dump; my %h; dd \%h; $_ = "a_b_c"; $h{/a_(.*)_c/ +} = "boom_$1"; dd \%h' {} Use of uninitialized value $1 in concatenation (.) or string at -e lin +e 1. { 1 => "boom_" }

    In scalar context (between the braces in $h{...}) you'll only count the number of captures (1 in this case), as opposed to returning the captured values. You'd need to supply a list context.

    $ perl -wE 'use Data::Dump; my %h; dd \%h; $_ = "a_b_c"; $h{(/a_(.*)_c +/)[0]} = "boom_$1"; dd \%h' {} Use of uninitialized value $1 in concatenation (.) or string at -e lin +e 1. { b => "boom_" }

    So, now we have the key (b in this case) but $1, in "boom_$1", is not initialised. The problem here is that the RHS of an assignment is evaluated before the LHS (left-hand side). The capture (to $1) occurs on the LHS, so $1 is not available when the RHS is evaluated.

    You don't actually want "b" as a key and trying to write your code like that clearly isn't going to work anyway. So, abandon that idea.

    If you add a cross-reference table and use a less specific regex, you can actually do this in one statement.

    You've been less than precise with exactly what you want. You mentioned "4 key-value pairs", but show a hash with eight and regexes to match six of them: this doesn't help us to provide a concrete solution. It's also unclear what the significance is of the leading and trailing parts of the keys; for instance, do all keys that start with "abc", end with "ghi"? are there other variations? if so, should they be handled differently?

    Because of all unknowns surrounding your question, consider the following code as a technique that you can adapt for your needs rather than a solution.

    #!/usr/bin/env perl use strict; use warnings; use Data::Dump; my %hash = ( xxx_text10_yyy => 1, xxx_text11_yyy => 1, mmm_text12_nnn => 1, mmm_text13_nnn => 1, abc_text6_ghi => 1, abc_text7_ghi => 1, abc_text8_ghi => 1, abc_text9_ghi => 1 ); my %xref = ( abc => 'boom', mmm => 'doom', xxx => 'zoom', ); my $re = qr{(?x: ^ ( [^_]+ ) _ ( [^_]+ ) )}; /$re/ and $hash{$_} = join '_', $xref{$1}, $2 for keys %hash; dd \%hash;

    Output:

    { abc_text6_ghi => "boom_text6", abc_text7_ghi => "boom_text7", abc_text8_ghi => "boom_text8", abc_text9_ghi => "boom_text9", mmm_text12_nnn => "doom_text12", mmm_text13_nnn => "doom_text13", xxx_text10_yyy => "zoom_text10", xxx_text11_yyy => "zoom_text11", }

    — Ken

      ... $1 is not available when the RHS is evaluated.

      Just as a side note, it's worse. Captures from a previous successful match are available:

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "'keep your powder dry' =~ /(pow)/; ;; $_ = 'a_b_c'; ;; my %h; $h{ (/a_(.*)_c/)[0] } = qq{boom_$1}; ;; dd \%h; " { b => "boom_pow" }
      And we glimpse the protean form of a nasty bug.


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

        Yes, his code zooms towards doom and goes boom. :-)

        — Ken

Log In?
Username:
Password:

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

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

    No recent polls found