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);
Related videos on Youtube
Comments
-
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 about 13 yearsWhat do you want to do in the case of multiple keys that map to the same value?
-
-
snoofkin about 13 yearsthe 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 about 13 yearsCompletely agreed. In trivial cases i think reverse is great, but it is not a universal solution as you highlighted.
-
snoofkin about 13 yearsWho said its wrong, just showing another way to do this, since reverse was already mentioned (-:
-
friedo about 13 yearsDuplicate values are overwritten with the while-loop version as well.
-
jsalonen about 13 yearsOf course they are, but within a while-loop you can more easily add code to handle duplicates...
-
Sam Watkins almost 12 yearsI 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 over 9 yearsIt would be conveniet to have an
invert()
method inHash::Util
that defaulted to pushing a HoA - or so did when it detected there were duplicate values. Maybe aperlcritic
warning that keys were being overwritten by duplicates that suggestedpush @{ $inverted{ { $hash{$_} }}, $_ for keys %hash;
would be better. Perl6's.invert
method requirespush
to avoid the overwritten duplicate issue as well. -
G. Cito over 9 yearsI guess there are patterns where you want duplicate values to overwritten into one key when reversing a hash.
-
G. Cito over 8 yearsThanks to mst and #perl-help for the shorthand :-)