Как я могу получить только часть хэша в Perl? - PullRequest
19 голосов
/ 11 июня 2009

Есть ли способ получить дополнительный хеш? Нужно ли использовать ломтик хеша?

Например:

%hash = ( a => 1, b => 2, c => 3 );

хочу только

%hash = ( a => 1, b => 2 );

Ответы [ 7 ]

52 голосов
/ 11 июня 2009

Хеш-фрагменты возвращают значения, связанные со списком ключей. Чтобы получить срез хэша, вы должны изменить символ на @ и предоставить список ключей (в данном случае "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/};

Многие люди будут использовать карту вместо кусков хеша:

my %new_hash = map { $_ => $hash{$_} } qw/a b/;

Начиная с Perl 5.20.0, вы можете получить ключи и значения за один шаг, если вы используете% sigil вместо @ sigil:

my %new_hash = %hash{qw/a b/};
9 голосов
/ 11 июня 2009

Возможно, вы захотите собрать список ключей, которые вам нужны:

my @keys = qw(a b);

А затем используйте цикл для создания хеша:

my %hash_slice;
for(@keys) {
  $hash_slice{$_} = %hash{$_};
}

Или:

my %hash_slice = map { $_ => $hash{$_} } @keys;

(я предпочитаю второй, но какой вам больше нравится.)

6 голосов
/ 11 июня 2009

Еще один способ:

my @keys = qw(a b);
my %hash = (a => 1, b => 2, c => 3);
my %hash_copy;
@hash_copy{@keys} = @hash{@keys};
3 голосов
/ 11 июня 2009

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 (который ожидает, в моем случае, хеш-адрес, содержащий имя пользователя и пароль, но ничего еще).

3 голосов
/ 11 июня 2009

Слишком много функционального программирования заставляет меня задуматься о zip.

С Список :: 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's 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. (Да, мутирование хеша во время итерации является безопасным ... пока мутация удаляет только самую последнюю итеративную пару.)

2 голосов
/ 21 января 2015

Новое в 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!
0 голосов
/ 11 июня 2009

Хеш - неупорядоченный контейнер, но термин «срез» действительно имеет смысл только с точки зрения упорядоченного контейнера. Может быть, посмотрите на использование массива. В противном случае вам, возможно, придется просто удалить все элементы, которые вы не хотите создавать для своего «подхеша».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...