Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Copy an hash modifying some selected values

by Anonymous Monk
on Nov 07, 2018 at 16:46 UTC ( #1225365=perlquestion: print w/replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I have an hash like this:
my %hash1 = ( key1 => 'relative_path', key2 => { key21 => 'another_relative_path', key22 => '/an_absolute_path', key23 => 'relative_path', }, key3 => '', key4 => 4 );
I want to create a new hash that is equal to %hash1, apart from some specific values that I want to check if these are absolute or relative paths. If they are relative paths I want to add some $home_path, For example I would want to check only these values:
$hash1{key1} $hash1{key2}{key22} $hash1{key2}{key23}
So to obtain:
my %hash2 = ( key1 => "$home_path/relative_path", key2 => { key21 => 'another_relative_path', key22 => '/an_absolute_path', key23 => "$home_path/relative_path", }, key3 => '', key4 => 4 );
It would be useful if I can define the list of values to check with some simple syntax, for example
@list = ('key1','key2:key22','key2:key23');
Thanks for any suggestion!

Replies are listed 'Best First'.
Re: Copy an hash modifying some selected values
by ikegami (Pope) on Nov 07, 2018 at 17:23 UTC

    Data::Diver comes in useful here.

    use Data::Diver qw( Dive ); my @paths = ('key1', 'key2:key22', 'key2:key23'); for my $path (@paths) { my @path = split(/:/, $path); my $val = Dive(\%hash, map \$_, @path); ... }

    It's not that hard to implement yourself, though.

    sub dive { my $r = shift; $r = $r->{shift(@_)} while $r && @_; return $r; } my @paths = ('key1', 'key2:key22', 'key2:key23'); for my $path (@paths) { my @path = split(/:/, $path); my $val = dive(\%hash, @path); ... }
      Thank you! The result for the code:
      use strict; use Data::Dumper; my %hash1 = ( key1 => 'relative_path', key2 => { key21 => 'another_relative_path', key22 => '/an_absolute_path', key23 => 'relative_path', }, key3 => '', key4 => 4 ); my @paths = ('key1', 'key2:key22', 'key2:key23'); for my $path (@paths) { my @path = split(/:/, $path); my $val = dive(\%hash1, @path); warn $val; } sub dive { my $r = shift; $r = $r->{shift(@_)} while $r && @_; return $r; }
      is:
      relative_path at test2.pl line 20. /an_absolute_path at test2.pl line 20. relative_path at test2.pl line 20.
      So I can get the values of interest to be checked, but how do I set them in the new %hash2?

        Did you look at the module I mentioned?

        use Data::Diver qw( DiveVal ); my @paths = ('key1', 'key2:key22', 'key2:key23'); for my $path (@paths) { my @path = split(/:/, $path); my $new_val = ...; DiveVal(\%hash, map \$_, @path) = $new_val; }

        or

        sub dive_val :lvalue { my $p = \shift; $p = \( $$p->{$_} ) for @_; $$p } my @paths = ('key1', 'key2:key22', 'key2:key23'); for my $path (@paths) { my @path = split(/:/, $path); my $new_val = ...; dive_val(\%hash, @path) = $new_val; }
Re: Copy an hash modifying some selected values
by Discipulus (Monsignor) on Nov 07, 2018 at 20:45 UTC
    Hello

    > I would want to check only these values...

    If you already have the list, just process it...

    for ( $hash1{key1}, $hash1{key2}{key22}, $hash1{key2}{key23} ){ $_ = File::Spec->file_name_is_absolute($_) ? $_ : File::Spec->rel2 +abs($_) }

    If you want to copy and modify, just copy the hash and modify the copy. Anyway Data::Diver is a very cool module, but when simpler solutions are use them.

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
      Thanks but the list is an input, so I needed something that is tackled as in ikegami's solution.
Re: Copy an hash modifying some selected values
by hippo (Canon) on Nov 08, 2018 at 09:31 UTC

    There will be a hatful of ways to approach this. Here's one using Data::DPath presented as an SSCCE:

    use strict; use warnings; use Test::More tests => 1; use Data::DPath 'dpathr'; my $home_path = '/home/foo'; my %hash1 = ( key1 => 'relative_path', key2 => { key21 => 'another_relative_path', key22 => '/an_absolute_path', key23 => 'relative_path', }, key3 => '', key4 => 4 ); my %hash2 = ( key1 => "$home_path/relative_path", key2 => { key21 => 'another_relative_path', key22 => '/an_absolute_path', key23 => "$home_path/relative_path", }, key3 => '', key4 => 4 ); my @list = ('/key1', '/key2/key22', '/key2/key23'); for my $keypat (@list) { foreach my $elem (dpathr ($keypat)->match (\%hash1)) { $$elem =~ s#^(?!/)#$home_path/#; }; } is_deeply (\%hash1, \%hash2);
      Maybe I don't get it but to me this is comparing two hashes instead of searching into a hash a creating a copy of it with modifications where required.

        Quite so. I assumed the copy was not the part you were having problems with. See eg merlyn's deep_copy sub as a module-less way to do that or something like Clone::PP. %hash1 is assumed to be the copy. HTH.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (4)
As of 2018-11-19 01:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My code is most likely broken because:
















    Results (206 votes). Check out past polls.

    Notices?