1
votes

I have two Perl arrays of equal length, for example-

@year = ('1995', '2003', '1997', '1995', '2012');
@title = ('dog', 'rabbit', 'tiger', 'lion', 'elephant');

I would like to convert them into a hash with years as key and titles as value so I can sort them, manipulate them etc, but converting them into a direct hash will remove the duplicates such as 1995 occurring twice.

What is the best way to convert this data into a hash while preserving duplicate instances of keys?

3
You could generate a hash of arrays, i.e. { key1 => (val1, val2), key2 => (val1), ... }.Kevin Richardson
hashes don't preserve duplicate keys. why do you want the data in a hash? how do you need to actually access it? what you should do depends on that.ysth

3 Answers

8
votes

Use hash of arrays (HoA):

#!/usr/bin/perl
use warnings;
use strict;

my @year  = qw(1995 2003 1997 1995 2012);
my @title = qw(dog rabbit tiger lion elephant);

my %hash;
for my $idx (0 .. $#year) {
    push @{ $hash{ $year[$idx] } }, $title[$idx];
}

for my $year (sort { $a <=> $b } keys %hash) {
    print "$year: ", join(', ', @{ $hash{$year} }), "\n";
}

Output:

1995: dog, lion
1997: tiger
2003: rabbit
2012: elephant
0
votes

The values in @title should probably be the keys then since they don't contain duplicates. If you still wanted to use a hash, that could look something like below where you use a hash slice to assign an array of values to an array of keys:

#!/usr/bin/env perl

use strict;
use warnings;

use Data::Dumper;

my @year = ('1995', '2003', '1997', '1995', '2012');
my @title = ('dog', 'rabbit', 'tiger', 'lion', 'elephant');

my %h;

@h{@title} = @year;
print Dumper \%h;

__END__
$VAR1 = {
          'tiger' => '1997',
          'elephant' => '2012',
          'dog' => '1995',
          'rabbit' => '2003',
          'lion' => '1995'
        };

If you elaborate more on how you want to sort your data, there may be an alternate solution that is better.

0
votes

"Slice assignment " makes this so easy to do when there's no duplicate keys or multiple values (e.g my %hash; @hash{@year}=@title) but keys with multiple values are a pretty typical data structure. The standard way to handle this when you want multiple values is with a hash of arrays (i.e where the values are anonymous array references surrounded by [ ]) as shown above by @choroba. But for reference, see also @miygawa's Hash::MultiValue which creates an object and a plain hash reference along with some methods for working with multiple values per key. It's pretty neat.

Here I use List::MoreUtils first to create the list of pairs for the constructor:

use Hash::MultiValue;
use List::MoreUtils qw(zip);

my @year  = qw(1995 2003 1997 1995 2012);
my @title = qw(dog rabbit tiger lion elephant);

my $hash = Hash::MultiValue->new( zip @year, @title );

Then to query a key use:

print join " " , $hash->get_all(1995)

output:

dog lion

c.f.