Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
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 pondering the Monastery: (4)
As of 2018-12-12 18:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How many stories does it take before you've heard them all?







    Results (60 votes). Check out past polls.

    Notices?
    • (Sep 10, 2018 at 22:53 UTC) Welcome new users!