The techniques and patterns covered in the other responses here are tried and true idioms that are essential for getting the most out of Perl, for understanding existing code, and for working with the large installed base of older perl compilers. Just for fun I thought I mention a couple of other approaches:
There's a fairly readable new syntax in perl-5.22 that is an alternative to the more classic approach take by @fugu. For something a bit more funky I'll mention @miyagawa's Hash::MultiValue. Perl 6 also has a nice way to convert lists of key/value pairs with potentially non-unique keys into hashes containing keys with multiple values.
As the other responses here point out, the "key" to all of this is:
For a hash key to refer to multiple values, the values need to be not just a list or array but a anonymous array [ ] or a reference.
Using new syntax available with perl-5.22
Fugu's response shows the standard Perl idiom. Iterating through @names using for 0 .. $#names ensures that overlapping keys are not "lost" and instead point at an anonymous array of multiple values. With perl-5.22 we can use the pairs() function from List::Util (a core module) and postfix dereferencing to add key/value pairs to a hash and account for overlapping or duplicate keys in a slightly different way:
use experimental qw(postderef);
use List::Util qw/pairs/;
my %hash;
my $a_ref = [ qw/tinku 15 tina 4 rita 18 tinku 18 tinku 17 tinku 16 rita 19/ ];
push $hash{$_->key}->@* , $_->value for pairs @$a_ref;
use DDP;
p %hash;
As of version 1.39 List::Util::pairs() returns ARRAY references as blessed objects accessible via ->key and ->value methods. The example uses LEONT's experimental.pm pragma and DDP to make things a bit more compact.
Output:
{
rita [
[0] 18,
[1] 19
],
tina [
[0] 4
],
tinku [
[0] 15,
[1] 18,
[2] 17,
[3] 16
]
}
As to which is more "readable": it's hard to beat the easily "grokable" standard approach, but with the new syntax available in the latest versions of perl5 we can explore the potential of new idioms. I am really starting to like postfix dereferencing. TIMTOWTDI and beyond!
@miyagawa's Hash::MultiValue
With this module all you can create a Hash::MultiValue object (with lots of methods to access it in various ways) and a plain hash reference to conveniently work with multiple values per key.
#!/usr/bin/env perl -l
use Hash::MultiValue;
use strict;
use warnings;
my $mvhash = Hash::MultiValue->new(tinku =>15, tina =>4, rita =>18,
tinku =>18, tinku =>17, tinku =>16, rita =>19);
print "\ntinku's values:\n", join " ", $mvhash->get_all('tinku');
print "\nflattened mvhash:\n", join " ", $mvhash->flatten ;
print "\n ... using mvhash as a hashref:" ;
print join " ", $mvhash->get_all($_) for keys %$mvhash ;
print "\n", '... as a "mixed" hashref with each():';
my $mvhash_ref = $mvhash->mixed ;
while ( my ($k, $v) = each $mvhash_ref ) {
print "$k => " , ref $v eq "ARRAY" ? "@{$v}" : "$v" ;
}
Output:
tinku's values:
15 18 17 16
flattened mvhash:
tinku 15 tina 4 rita 18 tinku 18 tinku 17 tinku 16 rita 19
... using mvhash as a hashref:
15 18 17 16
18 19
4
... as a "mixed" hashref with each():
tinku => 15 18 17 16
rita => 18 19
tina => 4
Once your hash is available as a Hash::MultiValue object you can manipulate it in various ways to quickly create temporary copies and hash references. Just assign them to a scalar and Dump the (or use DDP) to get an idea of how it works:
use DDP;
my $hmulti = $mvhash->multi; p $hmulti ;
my $hmixed = $mvhash->mixed; p $hmixed
There's some restrictions on using regular hash operations with a Hash::MultiValue object (and things like dd \$mvhash are not going to show you the whole hash - you need to do dd $hash->multi) however in some situations there is an advantage to working with multi-value hashes in this way (i.e. more readable and/or possibly less code needed for some functions).
You still need to recognize when/where Hash::MultiValue is useful so it's not unambiguously "easier" or "cleaner" - but it's another useful addition to your box of perl tools.
Perl 6 - just for comparison
Perl6 can be a bit more compact for grabbing key/value pairs from a list because you can use "multiple parameters" in a for statement, traversing a list by groups of elements then using push to arrange them into a hash. You can do this in a way that "automagically" accounts for overlapping keys. cf. this short perl6 snippet:
my %h ;
for <tinku 15 tina 4 rita 18 tinku 18 tinku 17 tinku 16 rita 19> -> $k, $v {
%h.push($k => $v) ;
}
%h.perl.say ;
Edit:
The friendly folks on #perl6 suggest an even more succinct "method":
my %h.push: <tinku 15 tina 4 rita 18 tinku 18 tinku 17 tinku 16 rita 19>.pairup ;
%h.perl.say ;
Output:
{:rita(["18", "19"]), :tina("4"), :tinku(["15", "18", "17", "16"])}<>
cf.
It's not just continued development of perl the compiler that makes it possible to write Perl code in new and interesting ways. Thanks to @miygawa and Paul Evans for his stewardship of Scalar-List-Utils you can do cool things with Hash::MultiValue even if your version of perl is as old as version 5.8; and you can try the functions available in latest versions of List::Util even if your perl is barely from this millennium (List::Util works with perl-5.6 which ushered in the 21st century in March 2000).