Perl grep nested hashes recursive
I have a structure that looks like this (hash of hashes):
%hash=( Level1_1=> { Level2_1 => "val1", Level2_2=> { Level3_1 => "val2", Level3_2 => "val1", Level3_3 => "val3", }, Level2_3 => "val3", }, Level1_2=> { Level2_1 => "val1", Level2_2=> { Level3_1 => "val1", Level3_2 => "val2", Level3_3 => "val3", }, Level2_3 => "val3", }, Level1_3=> { Level2_1 => "val1", Level2_2 => "val2", Level2_3 => "val3", }); I would like to grep this nested structure filtered by "val2" And the output should be:
%result=( Level1_1=> { Level2_2=> { Level3_1 => "val2"} }, Level1_2=> { Level2_2=> { Level3_2 => "val2" } }, Level1_3=> { Level2_2 => "val2" } ); My first idea was to use a recursive routine as follows:
hashwalk_v( \%hash ); sub hashwalk_v { my ($element, @array) = @_; if( ref($element) =~ /HASH/ ) { while (my ($key, $value) = each %$element) { if( ref($value) =~ /HASH/ ) { push (@array, $key); hashwalk_v($value, @array); } else { if ( $value =~ "val2") { push (@array, $key); print $_ . "\n" for @array; } else { @array =""; } } } } } but unfortunately I cannot save the hash key from the previous loop. Any ideas?
Similar approach
use Data::Dumper; print Dumper hfilter(\%hash, "val2"); sub hfilter { my ($h, $find) = @_; return if ref $h ne "HASH"; my %ret = map { my $v = $h->{$_}; my $new = ref($v) && hfilter($v, $find); $new ? ($_ => $new) : $v eq $find ? ($_ => $v) : (); } keys %$h; return %ret ? \%ret : (); } Recursion is the correct answer, but you seem to be a little confused along the way. :) Create a new hash when you go, one level at a time:
sub deep_hash_grep { my ($hash, $needle) = @_; my $ret = {}; while (my ($key, $value) = each %{$hash}) { if (ref $value eq 'HASH') { my $subgrep = deep_hash_grep($value, $needle); if (%{$subgrep}) { $ret->{$key} = $subgrep; } } elsif ($value =~ $needle) { $ret->{$key} = $value; } } return $ret; }