You were close. Remember that $hash2{major}{$test} is a scalar, not a hash link.
#! /usr/bin/perl use strict; use warnings; my %hash1 = ( test1 => { inner1 => { more => "alpha", evenmore => "beta" } }, test2 => { inner2 => { more => "charlie", somethingelse => "delta" } }, test3 => { inner9999 => { ohlookmore => "golf", somethingelse => "foxtrot" } } ); my %hash2 = ( major => { test2 => "inner2", test3 => "inner3" } ); foreach my $k (keys %hash1) { my $delete = 1; foreach my $inner (keys %{ $hash1{$k} }) { $delete = 0, last if exists $hash2{major}{$k} && $hash2{major}{$k} eq $inner; } delete $hash1{$k} if $delete; } use Data::Dumper; $Data::Dumper::Indent = 1; print Dumper \%hash1;
The line starting with $delete = 0, ... is a bit similar. This is equivalent to $delete = 0; last; $delete = 0; last; within another conditional expression, but it is already nested twice. Not wanting to create a matryoshka doll , I used the operator modifier , but, as the name suggests, it modifies one operator.
What is where the Pera comma operator comes in:
Binary is a comma operator. In a scalar context, it evaluates the left argument, returns that value, then evaluates its right argument and returns that value. It looks like a C-comma.
In this case, the left argument is the expression $delete = 0 , and the right argument is last .
A conditional may seem overly fussy, but
... if $hash2{major}{$k} eq $inner;
issues undefined -value warnings when testing for tests not mentioned in %hash2 (for example, test1 / inner1). Using
.. if $hash2{major}{$k} && $hash2{major}{$k} eq $inner;
would incorrectly delete the test mentioned in %hash2 if its "internal name" was a false value, such as the string "0" . Yes, using exists here can be uselessly fussy, but without knowing your actual hash keys, I chose a conservative route.
Conclusion:
$ VAR1 = {
'test2' => {
'inner2' => {
'somethingelse' => 'delta',
'more' => 'charlie'
}
}
}; Although you are not breaking it, remember the following caution regarding using each :
If you add or remove hash elements while iterating over it, you can get missing or duplicate entries, so no. Exception: you can always delete an item that was recently returned by each , which means that the following code will work:
while (($key, $value) = each %hash) { print $key, "\n"; delete $hash{$key};
Update: Finding hashes as if they were arrays (impress your CS fasteners by saying "... linearly, not logarithmically") is a red flag, and the code above does just that. The best approach that turns out to be similar to Penfold's answer is
%hash1 = map +($_ => $hash1{$_}), grep exists $hash2{major}{$_} && exists $hash1{$_}{ $hash2{major}{$_} }, keys %hash1;
In a pleasant declarative style, it describes the desired contents of %hash1 , namely
- first level keys
%hash1 should be mentioned in $hash2{major} and - the value in
$hash2{major} corresponding to each key of the first level should be a subsection of this key back to %hash1
(Wow, dizzy. We need some placeholder variables in English!)
The unary plus in +($_ => $hash1{$_}) removes the ambiguity for a weak parser, so it knows that we want the expression to be treated as a βpairβ. See the end of the perlfunc documentation on map for other cases where this may be necessary.