in reply to Recursively walk a hash to get to an element

The following is the same solution but faster since it eliminates all those slow sub calls:

```sub DiveRef {
my \$p = \shift;
\$p = \( \$\$p->{\$_} ) for @_;
\$p
}

my %myhash;
my \$ref = DiveRef(\%myhash, qw( bedrock flintstone fred ));
-or--
my \$myhash;
my \$ref = DiveRef(\$myhash, qw( bedrock flintstone fred ));

\$\$ref = 42; # set it to 42
\$\$ref++; # increment it
print \$\$ref; # print it!
[download]```

Replies are listed 'Best First'.
Re^2: Recursively walk a hash to get to an element
by LanX (Cardinal) on Apr 04, 2021 at 19:20 UTC
Adding some syntactic sugar with prototype and lvalue (not sure when the + prototype was introduced UPDATE: 5.14.0 code corrected)

Checking the ref-type would help diving arrays too. (but would probably reinvent Data::Diver )

```use v5.14;
use warnings;
use Test::More;

sub dive(+;@) :lvalue {
my \$h = \ shift;

\$h = \(\$\$h->{\$_}) for @_;

\$\$h;
}

my %hsh;
my \$h_ref = \%hsh;

my \$val = \$hsh{a}{b}{c} = 3;
my \$val2 = 42;

my @path = qw/a b c/;

my \$test;

;;;;;;;;;; \$test = "Prototypes";

is( dive(%hsh  => @path) , \$val, "\$test accepts \%list form");
is( dive(\$h_ref=> @path) , \$val, "\$test accepts \\$ref  form");

;;;;;;;;;; \$test = "assign directly to lvalue";

dive(%hsh=> @path) = \$val2;

is( \$hsh{a}{b}{c}, \$val2, \$test);

;;;;;;;;;; \$test = "increment in place";

dive( %hsh=> @path )++;

is( \$hsh{a}{b}{c}, ++\$val2, \$test);

;;;;;;;;;; \$test = "grab scalar reference";

my \$s_ref = \ dive(%hsh, @path);

\$\$s_ref++;

is( \$hsh{a}{b}{c}, ++\$val2, \$test);

;;;;;;;;;; \$test = "aliasing";

for my \$alias ( dive(%hsh, @path) ) {
\$alias++;
}

is( \$hsh{a}{b}{c}, ++\$val2, \$test);

done_testing;
[download]```
OUTPUT:
```ok 1 - Prototypes accepts %list form
ok 2 - Prototypes accepts \$ref  form
ok 3 - assign directly to lvalue
ok 4 - increment in place
ok 5 - grab scalar reference
ok 6 - aliasing
1..6
[download]```

UPDATE: cosmetic corrections

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

The code I posted was based on code that did use :lvalue, but I adapted it to match the OP.

I strongly recommend using an existing solution such as Data::Diver.

```use Data::Diver qw( DiveVal );

my @keys = map "level\$_", 1 .. 3;

my \$branch = {};
DiveVal(\$branch, map \\$_, @keys) = 'leaf';
-or-
my %branch;
DiveVal(\%branch, map \\$_, @keys) = 'leaf';
[download]```

Obviously, it can be done without module too.

```sub DiveVal :lvalue {
my \$p = \shift;
\$p = \( \$\$p->{\$_} ) for @_;
\$\$p
}

my @keys = map "level\$_", 1 .. 3;

my \$branch;
DiveVal(\$branch, @keys) = 'leaf';
-or-
my %branch;
DiveVal(\%branch, @keys) = 'leaf';
[download]```

How my DiveVal works:

```Pre-loop:          \$p references \$branch
After loop pass 0: \$p references \$branch->{level1}
After loop pass 1: \$p references \$branch->{level1}{level2}
After loop pass 2: \$p references \$branch->{level1}{level2}{level3}
Returned:          \$branch->{level1}{level2}{level3}
[download]```

The extra level of indirection has many benefits.

• It removes the need to treat the last key specially.
• It removes the need to create the hash before it's dereferenced.
• It removes the need for the root to be a reference to a hash. Instead, any scalar can be the root, even an undefined one.
• It makes it easy to extend DiveVal to support mixed array/hash structures.

Being fundamentally the same solution, the explanation applies to Merlyn's code too. Just substitute \$a for \$p, and it's a reference to the final scalar that's returned instead of the scalar itself as an lvalue.