Inverting a Hash's Key and Values in Perl

19,205

Solution 1

Adapted from http://www.dreamincode.net/forums/topic/46400-swap-hash-values/:

Assuming your hash is stored in $hash:

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

%hash=%hash2;

Seems like much more elegant solution can be achieved with reverse (http://www.misc-perl-info.com/perl-hashes.html#reverseph):

%nhash = reverse %hash;

Note that with reverse, duplicate values will be overwritten.

Solution 2

Use reverse:

use Data::Dumper;

my %hash = ('month', 'may', 'year', '2011');
print Dumper \%hash;
%hash = reverse %hash;
print Dumper \%hash;

Solution 3

As mentioned, the simplest is

my %inverse = reverse %original;

It "fails" if multiple elements have the same value. You could create an HoA to handle that situation.

my %inverse;
push @{ $inverse{ $original{$_} } }, $_ for keys %original;

Solution 4

So you want reverse keys & vals in a hash? So use reverse... ;)

%hash2 = reverse %hash;

reverting (k1 => v1, k2 => v2) - yield (v2=>k2, v1=>k1) - and that is what you want. ;)

Solution 5

my %orig_hash = (...);
my %new_hash;

%new_hash = map { $orig_hash{$_} => $_ } keys(%orig_hash);
Share:
19,205

Related videos on Youtube

Steffan Harris
Author by

Steffan Harris

I'm a student programmer.

Updated on July 22, 2021

Comments

  • Steffan Harris
    Steffan Harris almost 3 years

    I would like to make the value the key, and the key the value. What is the best way to go about doing this?

    • Greg Bacon
      Greg Bacon about 13 years
      What do you want to do in the case of multiple keys that map to the same value?
  • snoofkin
    snoofkin about 13 years
    the reverse way is nice, but it has some cavets (pasted directly from the perl docs): If a value is duplicated in the original hash, only one of those can be represented as a key in the inverted hash. Also, this has to unwind one hash and build a whole new one, which may take some time on a large hash, such as from a DBM file.
  • jsalonen
    jsalonen about 13 years
    Completely agreed. In trivial cases i think reverse is great, but it is not a universal solution as you highlighted.
  • snoofkin
    snoofkin about 13 years
    Who said its wrong, just showing another way to do this, since reverse was already mentioned (-:
  • friedo
    friedo about 13 years
    Duplicate values are overwritten with the while-loop version as well.
  • jsalonen
    jsalonen about 13 years
    Of course they are, but within a while-loop you can more easily add code to handle duplicates...
  • Sam Watkins
    Sam Watkins almost 12 years
    I tested this. "reverse" uses slightly more memory than your "while" solution. The reverse solution is much quicker for small hashes, and generally a little quicker, because reverse is built in. For really big hashes, reverse is slower due to memory use. But the hashes themselves are much larger than the temporary memory needed by reverse. It would be a mistake to use reverse to copy between two huge tied 'virtual hashes' such as DBM files. The 'map' method uses the most memory and runs slowest. In general, I would use reverse.
  • G. Cito
    G. Cito over 9 years
    It would be conveniet to have an invert() method in Hash::Util that defaulted to pushing a HoA - or so did when it detected there were duplicate values. Maybe a perlcritic warning that keys were being overwritten by duplicates that suggested push @{ $inverted{ { $hash{$_} }}, $_ for keys %hash; would be better. Perl6's .invert method requires push to avoid the overwritten duplicate issue as well.
  • G. Cito
    G. Cito over 9 years
    I guess there are patterns where you want duplicate values to overwritten into one key when reversing a hash.
  • G. Cito
    G. Cito over 8 years
    Thanks to mst and #perl-help for the shorthand :-)