Printing Hash Pairs in Perl - perl

Please what is the difference between these two hash initialization methods?
The First Method:
$items{"food"} = "4.4";
$items{"water"} = "5.0";
$items{"shelter"} = "1.1";
foreach $item (keys $items) {
print "$item\n";
}
The output is:
food
water
shelter
The Second Method:
%items = {
'food' => '4.4',
'water' => '5.0',
'shelter' => '1.1'
};
foreach $item (keys %items) {
print "$item\n";
}
The output is a hash reference:
HASH(0x8cc41bc)
Why does the second method return a reference instead of the actual values?

Because you have misunderstood what {} does.
It creates an anonymous hash, returning a reference.
What you've just done is functionally similar to:
my %stuff = (
'food' => '4.4',
'water' => '5.0',
'shelter' => '1.1'
);
my %items = \%stuff;
Which doesn't make a lot of sense.
Use () to init the hash, and it'll work just fine.

This is a good example of why you should always turn on warnings in your Perl code.
$ perl -Mwarnings -E'%h = {}; say keys %h'
Reference found where even-sized list expected at -e line 1.
HASH(0xbb6d48)
For more detailed explanation, use "diagnostics" too.
$ perl -Mwarnings -Mdiagnostics -E'%h = {}; say keys %h'
Reference found where even-sized list expected at -e line 1 (#1)
(W misc) You gave a single reference where Perl was expecting a list
with an even number of elements (for assignment to a hash). This usually
means that you used the anon hash constructor when you meant to use
parens. In any case, a hash requires key/value pairs.
%hash = { one => 1, two => 2, }; # WRONG
%hash = [ qw/ an anon array / ]; # WRONG
%hash = ( one => 1, two => 2, ); # right
%hash = qw( one 1 two 2 ); # also fine
HASH(0x253ad48)

Related

Perl Hash References - Is it possible to put reference to nested hash into 1 variable?

I have a partially nested hash like the following:
$href = {one=>1, word_counts=>{"the"=>34, "train"=>4} };
and I would like to get the value of $href->{'word_counts'}{'train'}.
Is it possible to put the {'word_counts'}{'train'} into a variable, so I can access it by simply calling $href->$variable?
No, but you can use Data::Diver to get a value given a list of keys:
my #keys = ('word_counts', 'train');
my $value = Data::Diver::Dive($href, \(#keys));
There are various ways to do this. I don't think you need to involved $href once you have a shortcut to the value that you want.
You can take a reference to the value, but then you have to dereference it:
my $value_ref = \ $href->{'word_counts'}{'train'};
say $$value_ref;
There's an experimental refaliasing feature where both sides are a reference. Now you don't need to dereference:
use v5.22;
\ my $value_ref = \ $href->{'word_counts'}{'train'};
say $value_ref; # 4
$value_ref = 17;
say $href->{'word_counts'}{'train'}; # 17
It's not hard to walk the hash yourself. The trick is to get one level of the hash, store it in a variable, then use that variable to get the next level. Keep going until you are where you want to be:
my $href = {
one => 1,
word_counts => {
"the" => {
"dog" => 45,
"cat" => 24,
},
"train" => {
"car" => 7,
"wreck" => 37,
}
}
};
my #keys = qw( word_counts train car );
my $temp = $href;
foreach my $key ( #keys ) {
die "Not a hash ref at <$key>" unless ref $temp eq ref {};
die "<$key> not in the hash" unless exists $temp->{$key};
$temp = $temp->{$key};
}
print "Value is <$temp>"; # 7
In addition to the more general, excellent answers from ysth and brian d foy, consider also a very simple (perhaps too simple) solution:
my #keys = qw( word_counts train);
print $href->{ $keys[0] }{ $keys[1] }; # 4
Note that this solution is repetitive, not elegant (the order of keys is hardcoded), and does not try to walk the hash. But depending on the context and the specific task of the OP, this may be all that is needed.

Different declaration of hash in Perl

I know that:
my %hash = {};
gets an anonymous hash, How about this:
my %hash = %{some values}
what is the difference of that above with this?
my %hash = (some hash values);
No.
my %hash = {};
generates a warning (you turned them on, right?):
Reference found where even-sized list expected at -e line 1.
Reference is always scalar. The correct way is
my $hash_ref = {};
To dereference a reference, you can use the following syntax:
my %hash = %$hash_ref;
my %also_hash = %{$hash_ref}; # Same as above.
$hash{key} eq $hash_ref->{key} or die; # Should survive.
Moreover,
%{ some values }
generates a syntax error:
perl -we 'my $h = %{1, 2, 3, 4}'
syntax error at -e line 1, near "%{"
The difference is how you express the content of the hash.
With the array notation below, for example; you do it like this:
my %hash = ( 'key 1' => 'value 1', 'key 2' => 'value 2');
The %{ } is a cast operator. It is used typically when you have a reference to something that is not obviously a hash. Typically a reference to a has:
Example:
my $hashref;
$hashref->{'key 1'}='value 1';
$hashref->{'key 2'}='value 2';
my %hash = %{$hashref};

Problems with hash of hashes in perl

I have a problem accessing variables in a hash of hashes I don't know what I have done wrong. While debugging the value of hash %list1 gives an undef, So I can't get my values out .
use strict ;
use warnings ;
my $text = "The, big, fat, little, bastards";
my $Author = "Alex , Shuman ,Directory";
my %hashes = {1,2,3,40};
my %count = ();
my #lst = split(",",$text);
my $i = 0 ;
my #Authors = split(",", $Author);
foreach my $SingleAuthor(#Authors)
{
foreach my $dig (#lst)
{
$count{$SingleAuthor}{$dig}++;
}
}
counter(\%count);
sub counter
{
my $ref = shift;
my #SingleAuthors = keys %$ref;
my %list1;
foreach my $SingleAuthor1(#SingleAuthors)
{
%list1 = $ref->{$SingleAuthor1};
foreach my $dig1 (keys %list1)
{
print $ref->{$SingleAuthor1}->{$dig1};
}
}
}
In two places you are attempting to assign a hash reference to a hash, which results in this warning: Reference found where even-sized list expected.
Here are two edits you need:
# I changed {} to ().
# However, you never use %hashes, so I'm not sure why it's here at all.
my %hashes = (1,2,3,40);
# I added %{} around the right-hand side.
%list1 = %{$ref->{$SingleAuthor1}};
See perlreftut for a useful and brief discussion of complex data structures.
For what it's worth, your counter() method could be simplified without loss of readability by dropping the intermediate variables.
sub counter {
my $tallies = shift;
foreach my $author (keys %$tallies) {
foreach my $dig (keys %{$tallies->{$author}}) {
print $tallies->{$author}{$dig}, "\n";
}
}
}
Or, as ysth points out, like this if you don't need the keys:
foreach my $author_digs (values %$tallies) {
print $dig, "\n" for values %$author_digs;
}
When I ran your code, Perl told me exactly what the problem was.
$ ./hash
Reference found where even-sized list expected at ./hash line 7.
Reference found where even-sized list expected at ./hash line 30.
Use of uninitialized value in print at ./hash line 34.
Reference found where even-sized list expected at ./hash line 30.
Use of uninitialized value in print at ./hash line 34.
Reference found where even-sized list expected at ./hash line 30.
Use of uninitialized value in print at ./hash line 34.
If that's unclear, you can turn on diagnostics to get more detailed descriptions.
$ perl -Mdiagnostics hash
Reference found where even-sized list expected at hash line 7 (#1)
(W misc) You gave a single reference where Perl was expecting a list
with an even number of elements (for assignment to a hash). This usually
means that you used the anon hash constructor when you meant to use
parens. In any case, a hash requires key/value pairs.
%hash = { one => 1, two => 2, }; # WRONG
%hash = [ qw/ an anon array / ]; # WRONG
%hash = ( one => 1, two => 2, ); # right
%hash = qw( one 1 two 2 ); # also fine

map for hashes in Perl

Is there a hash equivalent for map?
my %new_hash = hash_map { new_key($a) => new_val($b) } %hash;
I know that I could loop through the keys.
List::Pairwise claims to implement exactly that syntax -- see mapp, grepp. I haven't used it though.
Also, you can do it as
%new_hash = map { new_key($_) => new_value($hash{$_}) } keys %hash;
which I admit looks clumsier if %hash is really a $deeply->{buried}->{hash}. I prefer using $temp = ...; map {...} keys %$temp in such cases.
I really can’t see what you are trying to do here. What does “a hash equivalent for map” even mean? You can use map on a hash just fine. If you want the keys, just use keys; for example"
#msglist = map { "value of $_ is $hash{$_}" } keys %hash
although usually
say "value of $_ is $hash{$_}" keys %hash;
is just fine.
If you want both, then use the whole hash.
For assignment, what’s wrong with %new_hash = %old_hash?
Do you have deep-copy issues? Then use Storable::dclone.
Do you want both key and value available in the closure at the same time? Then make a bunch of pairs with the first map:
#pairlist = map { [ $_ => $hash{$_} ] } keys %hash
I need to see an example of what you would want to do with this, but so far I can see zero cause for using some big old module instead of basic Perl.
You can use map like this:
my $i = 0;
my %new_hash = map { $i ^= 1 ? new_key($_) : new_val($_) } %hash;
You can use mapn from my module List::Gen to do this:
use List::Gen 'mapn';
my %new_hash = mapn {new_key($_[0]) => new_value($_[1])} 2 => %old_hash;
mapn is like map, except it it takes an additional argument, the number of elements to walk the list by. Inside the block, the #_ array is set to the current slice.
$ perl -d /dev/null
DB<2> %p = ( a=>'b', c=> 'd');
DB<5> p Dumper \%p
$VAR1 = {
'c' => 'd',
'a' => 'b'
};
To e.g. reverse the key and the value:
DB<6> %q = map { ($p{$_}, $_ ) } keys %p
DB<7> p Dumper \%q
$VAR1 = {
'b' => 'a',
'd' => 'c'
};
As of perl 5.20, core utility List::Util::pairmap does exactly that:
use List::Util qw(pairmap);
my %new_hash = pairmap { new_key($a) => new_val($b) } %hash;
It's not necessarily optimal (as it involves unrolling the hash to a list and back) but I believe this is the shortest way in vanilla perl.

How do I pass a hash to a function in Perl?

I have a function that takes a variable and an associative array, but I can't seem to get them to pass right. I think this has something to do with function declarations, however I can't figure out how they work in Perl. Is there a good reference for this and how do I accomplish what I need?
I should add that it needs to be passed by reference.
sub PrintAA
{
my $test = shift;
my %aa = shift;
print $test . "\n";
foreach (keys %aa)
{
print $_ . " : " . $aa{$_} . "\n";
$aa{$_} = $aa{$_} . "+";
}
}
Pass the reference instead of the hash itself. As in
PrintAA("abc", \%fooHash);
sub PrintAA
{
my $test = shift;
my $aaRef = shift;
print $test, "\n";
foreach (keys %{$aaRef})
{
print $_, " : ", $aaRef->{$_}, "\n";
}
}
See also perlfaq7: How can I pass/return a {Function, FileHandle, Array, Hash, Method, Regex}?
This code works:
#!/bin/perl -w
use strict;
sub PrintAA
{
my($test, %aa) = #_;
print $test . "\n";
foreach (keys %aa)
{
print $_ . " : " . $aa{$_} . "\n";
}
}
my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA );
PrintAA("test", %hash);
The key point is the use of the array context in the my() 'statement' in the function.
What does the array context business actually do?
Succinctly, it makes it work correctly.
It means that the first value in the #_ array of arguments is assigned to $test, and the remaining items are assigned to the hash %aa. Given the way I called it, there is an odd number of items in the #_, so once the first item is assigned to $test, there is an even number of items available to assign to %aa, with the first item of each pair being the key ('aaa', 'bbb', 'ccc' in my example), and the second being the corresponding value.
It would be possible to replace %aa with #aa, in which case, the array would have 6 items in it. It would also be possible to replace %aa with $aa, and in that case, the variable $aa would contain the value 'aaa', and the remaining values in #_ would be ignored by the assignment.
If you omit the parentheses around the variable list, Perl refuses to compile the code.
One of the alternative answers showed the notation:
my $test = shift;
my(%aa) = #_;
This is pretty much equivalent to what I wrote; the difference is that after the two my statements, #_ only contains 6 elements in this variation, whereas in the single my version, it still contains 7 elements.
There are definitely other questions in SO about array context.
Actually, I wasn't asking about the my($test, %aa) = #_; I was asking about my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA ); versus my %hash = { 'aaa' => 1, ... };
The difference is that the { ... } notation generates a hash ref and the ( ... ) notation generates a list, which maps to a hash (as opposed to hash ref). Similarly, [ ... ] generates an array ref and not an array.
Indeed, change the 'main' code so it reads: my(%hash) = { ... }; and you get a run-time (but not compile time) error - treat line numbers with caution since I've added alternative codings to my file:
Reference found where even-sized list expected at xx.pl line 18.
...
Use of uninitialized value in concatenation (.) or string at xx.pl line 13.
Alternatively:
sub PrintAA
{
my $test = shift;
my %aa = #_;
print $test . "\n";
foreach (keys %aa)
{
print $_ . " : " . $aa{$_} . "\n";
$aa{$_} = $aa{$_} . "+";
}
}
The thing you're fundamentally missing is that an associative array isn't a single argument (though an associative array reference is, as in Paul Tomblin's answer).
It looks like you should pass in a reference to a hash.
sub PrintAA
{
my $test = shift;
my $aa = shift;
if (ref($aa) != "HASH") { die "bad arg!" }
....
}
PrintAA($foo, \%bar);
The reason you can't do a
my %aa = shift;
is because Perl flattens all the arguments to a subroutine into one list, #_. Every element is copied, so passing in by reference avoids those copies as well.
As usual there are several ways. Here is what Perl Best Practices, that most revered of style pointers, has to say about passing parameters to functions:
Use a hash of named arguments for any subroutine that has more than three parameters
But since you have only two, you could get away ;) with passing them directly like this:
my $scalar = 5;
my %hash = (a => 1, b => 2, c => 3);
func($scalar, %hash)
And function is defined like this:
sub func {
my $scalar_var = shift;
my %hash_var = #_;
... Do something ...
}
It could be more useful if you could show some code.
All the methods in previous answers work, but this was always the way I preferred to do things like this:
sub PrintAA ($\%)
{
my $test = shift;
my %aa = ${shift()};
print "$test\n";
foreach (keys %aa)
{
print "$_ : $aa{$_}\n";
$aa{$_} = "$aa{$_}+";
}
}
Note: I also changed your code a bit. Perl's double-quoted strings will interpret "$test" to be the value of $test rather than the actual string '$test', so you don't need that many .s.
Also, I was wrong about how the prototypes work. To pass a hash, use this:
PrintAA("test", %hash);
To print a hash reference, use this:
PrintAA("test", %$ref_to_hash);
Of course, now you can't modify the hash referenced by $ref_to_hash because you're sending a copy, but you can modify a raw %hash because you're passing it as a reference.
Arguments to functions get flattened into a single array (#_). So it's usually easiest to pass hashes to a function by reference.
To create a hash:
my %myhash = ( key1 => "val1", key2 => "val2" );
To create a reference to that hash:
my $href = \%myhash
To access that hash by reference;
%$href
So in your sub:
my $myhref = shift;
keys %$myhref;
All the other replies here so far seem rather complicated to me. When I write Perl function I usually "expand" all the passed arguments in the first line of the function.
sub someFunction {
my ( $arg1, $arg2, $arg3 ) = #_;
This is similar to other languages, where you declare functions as
... someFunction ( arg1, arg2, arg3 )
And if you do it that way and pass the hash as the last argument, you'll be fine without any tricks or special magic. E.g.:
sub testFunc {
my ( $string, %hash ) = #_;
print "$string $hash{'abc'} $hash{'efg'} $string\n";
}
my %testHash = (
'abc' => "Hello,",
'efg' => "World!"
);
testFunc('!!!', %testHash);
The output is as expected:
!!! Hello, World! !!!
This works because in Perl arguments are always passed as an array of scalar values and if you pass a hash, its key value/pairs are added to that array. In the sample above, the arguments passed to the function as array (#_) are in fact:
'!!!', 'abc', 'Hello,', 'efg', 'World!'
and '!!!' is simply assigned to %string, while %hash "swallows" all the other arguments, always interpreting one as a key and the next one as value (until all elements are used up).
You cannot pass multiple hashes that way and the hash cannot be the first argument, as otherwise it would swallow everything and leave all other arguments unassigned.
Of course, exactly the same works for array as a last argument. The only difference here is that arrays don't distinguish between keys and values. For them, all arguments left over are values and just get pushed to the array.
Use the following sub to get the hash or hashref - whatever is passed :)
sub get_args { ref( $_[0] ) ? shift() : ( #_ % 2 ) ? {} : {#_}; }
sub PrintAA
{
my $test = shift;
my $aa = get_args(#_);;
# Then
$aa->{somearg} # Do something
$aa->{anotherearg} # Do something
}
Call your function like this:
printAA($firstarg,somearg=>1, anotherarg=>2)
Or like this (no matter):
printAA($firstarg, {somearg=>1, anotherarg=>2})
Or even like this (no matter):
my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \PrintAA );
PrintAA("test", %hash);