Обращение ключа и значений хэша в Perl - PullRequest
25 голосов
/ 25 мая 2011

Я хотел бы сделать значение ключом, а ключ - значением. Каков наилучший способ сделать это?

Ответы [ 7 ]

20 голосов
/ 25 мая 2011

Адаптировано с http://www.dreamincode.net/forums/topic/46400-swap-hash-values/:

Если ваш хеш хранится в $hash:

while (($key, $value) = each %hash) {
   $hash2{$value}=$key;
}

%hash=%hash2;

Похоже, что гораздо более элегантное решение может быть достигнуто с помощью reverse (http://www.misc -perl-info.com / perl-hashes.html # reverseph ):

%nhash = reverse %hash;

Обратите внимание, что при обращении дублирующиеся значения будут перезаписаны.

15 голосов
/ 25 мая 2011

Использование reverse:

use Data::Dumper;

my %hash = ('month', 'may', 'year', '2011');
print Dumper \%hash;
%hash = reverse %hash;
print Dumper \%hash;
11 голосов
/ 25 мая 2011

Как уже упоминалось, самое простое -

my %inverse = reverse %original;

. Это "не удается", если несколько элементов имеют одинаковое значение.Вы можете создать HoA для решения этой ситуации.

my %inverse;
push @{ $inverse{ $original{$_} } }, $_ for keys %original;
2 голосов
/ 25 мая 2011

То есть вы хотите поменять ключи и значения в хэше?Так что используйте reverse ...;)

%hash2 = reverse %hash;

возврат (k1 => v1, k2 => v2) - yield (v2 => k2, v1 => k1) - и это то, что вы хотите;)

2 голосов
/ 25 мая 2011
my %orig_hash = (...);
my %new_hash;

%new_hash = map { $orig_hash{$_} => $_ } keys(%orig_hash);
1 голос
/ 02 мая 2013

Решение «карта поверх ключей» более гибкое. Что если ваше значение не является простым значением?

my %forward;
my %reverse;

#forward is built such that each key maps to a value that is a hash ref:
#{ a => 'something', b=> 'something else'}

%reverse = map { join(',', @{$_}{qw(a b)}) => $_ } keys %forward;
0 голосов
/ 09 ноября 2015

Вот способ сделать это, используя Hash::MultiValue.

use experimental qw(postderef);

sub invert {
  use Hash::MultiValue;
  my $mvh = Hash::MultiValue->from_mixed(shift);

  my $inverted;    
  $mvh->each( sub { push $inverted->{ $_[1] }->@* , $_[0] } ) ;
  return $inverted;
}

Чтобы проверить это, мы можем попробовать следующее:

my %test_hash = (
  q => [qw/1 2 3 4/],
  w => [qw/4 6 5 7/],
  e => ["8"],
  r => ["9"],
  t => ["10"],
  y => ["11"],
);

my $wow  = invert(\%test_hash);
my $wow2 = invert($wow);

use DDP;
print "\n \%test_hash:\n\n" ;
p %test_hash;
print "\n \%test_hash inverted as:\n\n" ;
p $wow ;

# We need to sort the contents of the multi-value array reference
# for the is_deeply() comparison:
map { 
   $test_hash{$_} = [ sort { $a cmp $b || $a <=> $b } @{ $test_hash{$_} } ] 
} keys %test_hash ; 

map { 
   $wow2->{$_} = [ sort { $a cmp $b || $a <=> $b } @{ $wow2->{$_} } ] 
} keys %$wow2 ; 

use Test::More ;
is_deeply(\%test_hash, $wow2, "double inverted hash == original");
done_testing;

Добавление

Обратите внимание, что для того, чтобы пройти здесь трюк с трюками, функция invert() опирается на %test_hash, имеющую ссылки на массивы в качестве значений. Чтобы обойти эту проблему, если ваши значения хеш-функции не являются ссылками на массив, вы можете "принудительно" преобразовать обычный / смешанный хэш в хэш-код из нескольких значений, который Hash::MultiValue может затем благословить в объекте. Однако этот подход означает, что даже отдельные значения будут отображаться как ссылки на массивы:

for ( keys %test_hash )  { 
     if ( ref $test_hash{$_} ne 'ARRAY' ) { 
           $test_hash{$_}  = [ $test_hash{$_} ] 
     } 
}

что означает:

ref($_) or $_ = [ $_ ] for values %test_hash ;

Это понадобится только для прохождения теста «туда-обратно».

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