Есть ли способ получить суб-хэш? Нужно ли использовать хэш-фрагмент?
Например:
%hash = ( a => 1, b => 2, c => 3 );
Я хочу только
%hash = ( a => 1, b => 2 );
Есть ли способ получить суб-хэш? Нужно ли использовать хэш-фрагмент?
Например:
%hash = ( a => 1, b => 2, c => 3 );
Я хочу только
%hash = ( a => 1, b => 2 );
Хэш-фрагменты возвращают значения, связанные со списком ключей. Чтобы получить хэш-срез, вы меняете сигил на @и предоставляете список ключей (в данном случае "a"
и "b"
):
my @items = @hash{"a", "b"};
Часто вы можете использовать оператор слова котировки для создания списка:
my @items = @hash{qw/a b/};
Вы также можете назначить хэш-фрагмент, поэтому, если вы хотите новый хэш, содержащий подмножество другого хэша, вы можете сказать
my %new_hash;
@new_hash{qw/a b/} = @hash{qw/a b/};
Многие люди будут использовать map вместо хэш-фрагментов:
my %new_hash = map { $_ => $hash{$_} } qw/a b/;
Начиная с Perl 5.20.0, вы можете получить ключи и значения за один шаг, если вместо синтаксиса @используется сигль%:
my %new_hash = %hash{qw/a b/};
Возможно, вам захочется собрать список желаемых ключей:
my @keys = qw(a b);
И затем используйте цикл для создания хэша:
my %hash_slice;
for(@keys) {
$hash_slice{$_} = %hash{$_};
}
Или:
my %hash_slice = map { $_ => $hash{$_} } @keys;
(Мое предпочтение - второе, но лучше, чем вам нравится).
Еще один способ:
my @keys = qw(a b);
my %hash = (a => 1, b => 2, c => 3);
my %hash_copy;
@hash_copy{@keys} = @hash{@keys};
Слишком много функционального программирования сначала заставляет меня думать о zip
.
С List:: MoreUtils установлен,
use List::MoreUtils qw(zip);
%hash = qw(a 1 b 2 c 3);
@keys = qw(a b);
@values = @hash{@keys};
%hash = zip @keys, @values;
К сожалению, прототип List:: MoreUtils zip
запрещает
zip @keys, @hash{@keys};
Если вы действительно хотите избежать промежуточной переменной, вы можете
zip @keys, @{[@hash{@keys}]};
Или просто напишите свой собственный zip
без проблемного прототипа. (Это не обязательно List:: MoreUtils вообще.)
sub zip {
my $max = -1;
$max < $#$_and $max = $#$_ for @_;
map { my $ix = $_; map $_->[$ix], @_; } 0..$max;
}
%hash = zip \@keys, [@hash{@keys}];
Если вы собираетесь мутировать на месте,
%hash = qw(a 1 b 2 c 3);
%keep = map +($_ => 1), qw(a b);
$keep{$a} or delete $hash{$a} while ($a, $b) = each %hash;
избегает дополнительного копирования, которое требует решения map
и zip
. (Да, мутируя хеш, когда вы повторяете его, это безопасно... до тех пор, пока мутация удаляет только последнюю итерационную пару.)
FWIW, я использую Moose:: Autobox здесь:
my $hash = { a => 1, b => 2, c => 3, d => 4 };
$hash->hslice([qw/a b/]) # { a => 1, b => 2 };
В реальной жизни я использую это, чтобы извлечь "имя пользователя" и "пароль" из представления формы и передать это Catalyst $c->authenticate
(который ожидает, в моем случае, hashref, содержащего имя пользователя и пароль, но ничего еще).
Новое в perl 5.20 - это хэш-срезы, возвращающие ключи, а также значения, используя%, как на последней строке:
my %population = ('Norway',5000000,'Sweden',9600000,'Denmark',5500000);
my @slice_values = @population{'Norway','Sweden'}; # all perls can do this
my %slice_hash = %population{'Norway','Sweden'}; # perl >= 5.20 can do this!
Хэш является неупорядоченным контейнером, но термин "срез" действительно имеет смысл только в терминах упорядоченного контейнера. Возможно, посмотрите на использование массива. В противном случае вам может потребоваться удалить все элементы, которые вы не хотите создавать "суб-хэш".