Вы были близки. Помните, что $hash2{major}{$test}
- это скаляр, а не ссылка на хеш.
#! /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;
Строка, начинающаяся с $delete = 0, ...
, немного неприятна. Это эквивалентно $delete = 0; last;
в другом условном выражении, но оно уже было вложено дважды. Не желая строить матрешку , я использовал модификатор оператора , но, как следует из названия, он модифицирует один оператор.
Вот где Оператор запятой Perl входит:
Binary ,
- оператор запятой. В скалярном контексте он оценивает свой левый аргумент, отбрасывает это значение, затем оценивает свой правый аргумент и возвращает это значение. Это так же, как оператор запятой C.
В этом случае левый аргумент - это выражение $delete = 0
, а правый аргумент - last
.
Условное выражение может показаться ненужным, но
... if $hash2{major}{$k} eq $inner;
выдает предупреждения с неопределенным значением при проверке тестов, не упомянутых в %hash2
(например, test1 / inner1). Использование
.. if $hash2{major}{$k} && $hash2{major}{$k} eq $inner;
неправильно удалит тест, упомянутый в %hash2
, если его «внутреннее имя» будет ложным значением, таким как строка "0"
. Да, использование exists
здесь может быть излишне суетливым, но, не зная ваших реальных хеш-ключей, я выбрал консервативный маршрут.
Выход:
$VAR1 = {
'test2' => {
'inner2' => {
'somethingelse' => 'delta',
'more' => 'charlie'
}
}
};
Хотя вы и не нарушаете его, имейте в виду следующее предостережение, касающееся использования each
:
Если вы добавляете или удаляете элементы хэша во время итерации по нему, вы можете пропустить или дублировать записи, так что не делайте этого. Исключение: всегда безопасно удалить элемент, последний раз возвращенный each
, что означает, что будет работать следующий код:
while (($key, $value) = each %hash) {
print $key, "\n";
delete $hash{$key}; # This is safe
}
Обновление: Поиск хэшей, как если бы они были массивами (поразите своих друзей-ботаников из CS, говоря «… линейно, а не логарифмически»), является красным флагом, и приведенный выше код делает именно это. Лучший подход, который оказывается похожим на ответ Пенфолда, это
%hash1 = map +($_ => $hash1{$_}),
grep exists $hash2{major}{$_} &&
exists $hash1{$_}{ $hash2{major}{$_} },
keys %hash1;
В хорошем декларативном стиле он описывает желаемое содержимое %hash1
, а именно
- ключи первого уровня
%hash1
должны быть упомянуты в $hash2{major}
и
- значение в
$hash2{major}
, соответствующее каждому ключу первого уровня, само должно быть подключом этого ключа обратно в %hash1