Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl accessing/changing deep hash by array list

Tags:

hash

perl

I want to delete an element of a hash(of any depth) which has the first key as $key[0], the second key as $key[1] etc, until @key is finished.

For example, if @key=(23,56,78) then i want to manipulate $hash{23}{56}{78}.
I don't know beforehand how many elements @key has.

I have been trying to use the following:

my %the_path;
my $temp=\%the_path;
for(my $cline=0;$cline<=$#keys;$cline++){
     my $cfolder=$keys[$cline];
     $temp->{$cfolder}={};
     $temp=$temp->{$cfolder};
}

But, I'm not sure how to manipulate the element at here. How do I do that?

like image 446
john-jones Avatar asked Mar 16 '26 14:03

john-jones


2 Answers

Data::Diver exists for exactly this purpose.

my $last_hash = Data::Diver::Dive( \%hash, @keys[0..$#keys-1] );
if ($last_hash) { delete $last_hash->{ $keys[-1] } }
like image 59
ysth Avatar answered Mar 18 '26 10:03

ysth


Here is an example with recursion:

use strict;
use warnings;

my $hash = { foo => { here => 'there', bar => { baz => 100 } } };

## mutates input
sub delete_hash {
  my ( $hash, $keys ) = @_;
  my $key = shift @$keys;
  die "Stopped recursing $key doesn't exist"
    unless exists $hash->{$key}
  ;
  scalar @$keys
    ? delete_hash( $hash->{$key}, $keys )
    : delete $hash->{$key}
  ;
}

delete_hash( $hash, [qw/foo bar/] );

use XXX;
YYY $hash;

The stack does grow and function calls have a price. You can aparently mitigate that with perl's version of TCO with this code:

if (scalar @$keys) {
  @_=($hash->{$key}, $keys);
  goto &delete_hash;
}
else {
  delete $hash->{$key}
}

And, it should also noted that none of this code prunes the tree: if you delete [qw/foo bar baz/] then bar will be an empty hash ref.

foo:
  bar: {}
  here: there
like image 21
NO WAR WITH RUSSIA Avatar answered Mar 18 '26 10:03

NO WAR WITH RUSSIA



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!