How can I reference a hash with a variable name? - perl

I have three hashes named %hash1, %hash2, %hash3. I need to reference each hash by variable and am not sure how to do it.
#!/usr/bin/perl
# Hashes %hash1, %hash2, %hash3 are populated with data.
#hashes = qw(hash1 hash2 hash3);
foreach $hash(#hashes){
foreach $key(keys $$hash){
.. Do something with the hash key and value
}
}
I know this is a fairly simplistic, comparatively noob question so my apologies for that.

This should work for you.
#!/usr/bin/perl
use strict;
use warnings;
my( %hash1, %hash2, %hash3 );
# ...
# load up %hash1 %hash2 and %hash3
# ...
my #hash_refs = ( \%hash1, \%hash2, \%hash3 );
for my $hash_ref ( #hash_refs ){
for my $key ( keys %$hash_ref ){
my $value = $hash_ref->{$key};
# ...
}
}
It uses hash references, instead of using symbolic references. It is very easy to get symbolic references wrong, and can be difficult to debug.
This is how you could have used symbolic references, but I would advise against it.
#!/usr/bin/perl
use strict;
use warnings;
# can't use 'my'
our( %hash1, %hash2, %hash3 );
# load up the hashes here
my #hash_names = qw' hash1 hash2 hash3 ';
for my $hash_name ( #hash_names ){
print STDERR "WARNING: using symbolic references\n";
# uh oh, we have to turn off the safety net
no strict 'refs';
for my $key ( keys %$hash_name ){
my $value = $hash_name->{$key};
# that was scary, better turn the safety net back on
use strict 'refs';
# ...
}
# we also have to turn on the safety net here
use strict 'refs';
# ...
}

To reference a hash by a reference you can do it one of two ways.
my $ref_hash = \%hash;
or create an anonymous referenced hash
my $ref_hash = {
key => value,
key => value
}
Now in order to access this hash you'll need to de-reference the variable or use the arrow syntax.
Example 1 (Arrow syntax)
print $ref_hash->{key};
Example 2
print ${$ref_hash}{key};
I hope that helps.

Related

How can multiple hash values be retrieved using perl?

How can multiple hash values be retrieved? I tried using
use Hash::MultiValue and get_all(). It throws an error saying "Can't call method "get_all" on an undefined value" . Which is the better option to implement this functionality of multiple values for a particular key ? The value of the key is the file that is being opened.
use warnings;
use List::MoreUtils qw(firstidx);
use Hash::MultiValue;
my $key_in;
…
open ($FR, "<$i") or die "couldn't open list";
while($line=<$FR>){
if($line =~ /search_pattern/){
my $idx = firstidx { $_ eq 'hash_key' } #tags;
my $key= #tags[$idx+1];
$hash{$key}= Hash::MultiValue->new($key=>'$i');
}
close($FR);
for my $key_in ( sort keys %hash ) {
#key_in = $hash->get_all('$key_in');
print "$key_in = $hash{$key_in}\n";
}
my $key_in = <STDIN>;
if (exists($hash{$key_in})){
$hash_value = $hash{$key_in};
}else{
exit;
}
I think you want an array reference for the value. You can then treat that as an array. This is the sort of stuff we show you in Intermediate Perl:
$hash{$key} = [];
push #{ $hash{$key} }, $some_value;
my #values = #{ $hash{$key} };
With Perl v5.24, you can use postfix dereferencing to make it a bit prettier:
use v5.24;
$hash{$key} = [];
push $hash{$key}->#*, 'foo';
push $hash{$key}->#*, 'bar';
my #values = $hash{$key}->#*;
And, since Perl automatically takes an undefined value and turns it into the reference structure you need (auto vivification), you don't need to initialize an undefined value:
use v5.24;
push $hash{$key}->#*, 'foo';
push $hash{$key}->#*, 'bar';
my #values = $hash{$key}->#*;
Get all the keys of a hash:
my #keys = keys %hash;
Get all of the values (in the order of the corresponding keys if you haven't changed the hash since you called keys):
my #values = values %keys;
Get some values with a hash slice:
my #some_values = #hash{#some_keys};
Get some keys and values (key-value slice):
use v5.20;
my %smaller_hash = %hash{#some_keys}
Here is an example of how you can use get_all() from Hash::MultiValue to retrive multiple hash values for a given key:
use strict;
use warnings;
use Data::Dumper qw(Dumper);
use Hash::MultiValue;
my $hash = Hash::MultiValue->new();
$hash->add(tag1 => 'file1');
$hash->add(tag1 => 'file2');
$hash->add(tag2 => 'file3');
my #foo = $hash->get_all('tag1');
print(Dumper(\#foo));
Output:
$VAR1 = [
'file1',
'file2'
];

Checking whether a string does not present in hash

I want to perform a check with the following condition.
If the member of %ans is not contained in %test, print that value of %ans.
But why this didn't print it?
use Data::Dumper;
my %ans = ("foo" => 1);
my %test = ("bar" => 1);
foreach my $ansrule ( keys %{$ans} ) {
if ( !exists $test{$ansrule} ) {
print "ANS: $ansrule\n";
}
}
https://eval.in/51453
Because keys %{$ans} is not the same as keys %ans, and you should've used the latter:
$ans and %ans are different variables.
The %{$ans} attempts to dereference a hash ref stored in $ans variable - which is, apparently, not defined. Have you added use strict; to your code, you'd have seen the warning...
Global symbol "$ans" requires explicit package name
You want
foreach my $ansrule ( keys %ans )
instead of
foreach my $ansrule ( keys %$ans )
use strict; use warnings; would be helpful in detection of such flaws.

Build hash of hash in perl

I'm new to using perl and I'm trying to build a hash of a hash from a tsv. My current process is to read in a file and construct a hash and then insert it into another hash.
my %hoh = ();
while (my $line = <$tsv>)
{
chomp $line;
my %hash;
my #data = split "\t", $line;
my $id;
my $iter = each_array(#columns, #data);
while(my($k, $v) = $iter->())
{
$hash{$k} = $v;
if($k eq 'Id')
{
$id = $v;
}
}
$hoh{$id} = %hash;
}
print "dump: ", Dumper(%hoh);
This outputs:
dump
$VAR1 = '1234567890';
$VAR2 = '17/32';
$VAR3 = '1234567891';
$VAR4 = '17/32';
.....
Instead of what I would expect:
dump
{
'1234567890' => {
'k1' => 'v1',
'k2' => 'v2',
'k3' => 'v3',
'k4' => 'v4',
'id' => '1234567890'
},
'1234567891' => {
'k1' => 'v1',
'k2' => 'v2',
'k3' => 'v3',
'k4' => 'v4',
'id' => '1234567891'
},
........
};
My limited understanding is that when I do $hoh{$id} = %hash; its inserting in a reference to %hash? What am I doing wrong? Also is there a more succint way to use my columns and data array's as key,value pairs into my %hash object?
-Thanks in advance,
Niru
To get a reference, you have to use \:
$hoh{$id} = \%hash;
%hash is the hash, not the reference to it. In scalar context, it returns the string X/Y wre X is the number of used buckets and Y the number of all the buckets in the hash (i.e. nothing useful).
To get a reference to a hash variable, you need to use \%hash (as choroba said).
A more succinct way to assign values to columns is to assign to a hash slice, like this:
my %hoh = ();
while (my $line = <$tsv>)
{
chomp $line;
my %hash;
#hash{#columns} = split "\t", $line;
$hoh{$hash{Id}} = \%hash;
}
print "dump: ", Dumper(\%hoh);
A hash slice (#hash{#columns}) means essentially the same thing as ($hash{$columns[0]}, $hash{$columns[1]}, $hash{$columns[2]}, ...) up to however many columns you have. By assigning to it, I'm assigning the first value from split to $hash{$columns[0]}, the second value to $hash{$columns[1]}, and so on. It does exactly the same thing as your while ... $iter loop, just without the explicit loop (and it doesn't extract the $id).
There's no need to compare each $k to 'Id' inside a loop; just store it in the hash as a normal field and extract it afterwards with $hash{Id}. (Aside: Is your column header Id or id? You use Id in your loop, but id in your expected output.)
If you don't want to keep the Id field in the individual entries, you could use delete (which removes the key from the hash and returns the value):
$hoh{delete $hash{Id}} = \%hash;
Take a look at the documentation included in Perl. The command perldoc is very helpful. You can also look at the Perldoc webpage too.
One of the tutorials is a tutorial on Perl references. It all help clarify a lot of your questions and explain about referencing and dereferencing.
I also recommend that you look at CPAN. This is an archive of various Perl modules that can do many various tasks. Look at Text::CSV. This module will do exactly what you want, and even though it says "CSV", it works with tab separated files too.
You missed putting a slash in front of your hash you're trying to make a reference. You have:
$hoh{$id} = %hash;
Probably want:
$hoh{$id} = \%hash;
also, when you do a Data::Dumper of a hash, you should do it on a reference to a hash. Internally, hashes and arrays have similar structures when a Data::Dumper dump is done.
You have:
print "dump: ", Dumper(%hoh);
You should have:
print "dump: ", Dumper( \%hoh );
My attempt at the program:
#! /usr/bin/env perl
#
use warnings;
use strict;
use autodie;
use feature qw(say);
use Data::Dumper;
use constant {
FILE => "test.txt",
};
open my $fh, "<", FILE;
#
# First line with headers
#
my $line = <$fh>;
chomp $line;
my #headers = split /\t/, $line;
my %hash_of_hashes;
#
# Rest of file
#
while ( my $line = <$fh> ) {
chomp $line;
my %line_hash;
my #values = split /\t/, $line;
for my $index ( ( 0..$#values ) ) {
$line_hash{ $headers[$index] } = $values[ $index ];
}
$hash_of_hashes{ $line_hash{id} } = \%line_hash;
}
say Dumper \%hash_of_hashes;
You should only store a reference to a variable if you do so in the last line before the variable goes go of scope. In your script, you declare %hash inside the while loop, so placing this statement as the last in the loop is safe:
$hoh{$id} = \%hash;
If it's not the last statement (or you're not sure it's safe), create an anonymous structure to hold the contents of the variable:
$hoh{$id} = { %hash };
This makes a copy of %hash, which is slower, but any subsequent changes to it will not effect what you stored.

How to use a 'subroutine reference' as a hash key

In Perl, I'm learning how to dereference 'subroutine references'. But I can't seem to use a subroutine reference as a hash 'key'.
In the following sample code,
I can create a reference to a subroutine ($subref) and then dereference it to run the subroutine (&$subref)
I can use the reference as a hash 'value' and then easily dereference that
But I cannot figure out how to use the reference as a hash 'key'. When I pull the key out of the hash, Perl interprets the key as a string value (not a reference) - which I now understand (thanks to this site!). So I've tried Hash::MultiKey, but that seems to turn it into an array reference. I want to treat it as a subroutine/code reference, assuming this is somehow possible?
Any other ideas?
use strict;
#use diagnostics;
use Hash::MultiKey;
my $subref = \&hello;
#1:
&$subref('bob','sue'); #okay
#2:
my %hash;
$hash{'sayhi'}=$subref;
&{$hash{'sayhi'}}('bob','sue'); #okay
#3:
my %hash2;
tie %hash2, 'Hash::MultiKey';
$hash2{$subref}=1;
foreach my $key (keys %hash2) {
print "Ref type is: ". ref($key)."\n";
&{$key}('bob','sue'); # Not okay
}
sub hello {
my $name=shift;
my $name2=shift;
print "hello $name and $name2\n";
}
This is what is returned:
hello bob and sue
hello bob and sue
Ref type is: ARRAY
Not a CODE reference at d:\temp\test.pl line 21.
That is correct, a normal hash key is only a string. Things that are not strings get coerced to their string representation.
my $coderef = sub { my ($name, $name2) = #_; say "hello $name and $name2"; };
my %hash2 = ( $coderef => 1, );
print keys %hash2; # 'CODE(0x8d2280)'
Tieing is the usual means to modify that behaviour, but Hash::MultiKey does not help you, it has a different purpose: as the name says, you may have multiple keys, but again only simple strings:
use Hash::MultiKey qw();
tie my %hash2, 'Hash::MultiKey';
$hash2{ [$coderef] } = 1;
foreach my $key (keys %hash2) {
say 'Ref of the key is: ' . ref($key);
say 'Ref of the list elements produced by array-dereferencing the key are:';
say ref($_) for #{ $key }; # no output, i.e. simple strings
say 'List elements produced by array-dereferencing the key are:';
say $_ for #{ $key }; # 'CODE(0x8d27f0)'
}
Instead, use Tie::RefHash. (Code critique: prefer this syntax with the -> arrow for dereferencing a coderef.)
use 5.010;
use strict;
use warnings FATAL => 'all';
use Tie::RefHash qw();
my $coderef = sub {
my ($name, $name2) = #_;
say "hello $name and $name2";
};
$coderef->(qw(bob sue));
my %hash = (sayhi => $coderef);
$hash{sayhi}->(qw(bob sue));
tie my %hash2, 'Tie::RefHash';
%hash2 = ($coderef => 1);
foreach my $key (keys %hash2) {
say 'Ref of the key is: ' . ref($key); # 'CODE'
$key->(qw(bob sue));
}
From perlfaq4:
How can I use a reference as a hash key?
(contributed by brian d foy and Ben Morrow)
Hash keys are strings, so you can't really use a reference as the key.
When you try to do that, perl turns the reference into its stringified
form (for instance, HASH(0xDEADBEEF) ). From there you can't get back
the reference from the stringified form, at least without doing some
extra work on your own.
Remember that the entry in the hash will still be there even if the
referenced variable goes out of scope, and that it is entirely
possible for Perl to subsequently allocate a different variable at the
same address. This will mean a new variable might accidentally be
associated with the value for an old.
If you have Perl 5.10 or later, and you just want to store a value
against the reference for lookup later, you can use the core
Hash::Util::Fieldhash module. This will also handle renaming the keys
if you use multiple threads (which causes all variables to be
reallocated at new addresses, changing their stringification), and
garbage-collecting the entries when the referenced variable goes out
of scope.
If you actually need to be able to get a real reference back from each
hash entry, you can use the Tie::RefHash module, which does the
required work for you.
So it looks like Tie::RefHash will do what you want. But to be honest, I don't think that what you want to do is a particularly good idea.
Why do you need it? If you e.g. need to store parameters to the functions in the hash, you can use HoH:
my %hash;
$hash{$subref} = { sub => $subref,
arg => [qw/bob sue/],
};
foreach my $key (keys %hash) {
print "$key: ref type is: " . ref($key) . "\n";
$hash{$key}{sub}->( #{ $hash{$key}{arg} } );
}
But then, you can probably choose a better key anyway.

How do I use symbolic references in Perl?

In Perl, if a variable holds the name for another variable, how do I use the first variable to visit the other one?
For example, let
$name = "bob";
#bob = ("jerk", "perlfan");
how should I use $name to get to know what kind of person bob is?
Although I'm not quite sure, my vague memory tells me it might related to typeglob.
Several points:
You aren't talking about typeglobs, you are talking about symbolic references.
Don't use symbolic references -- they lead to hard to track down bugs.
In almost any case where symbolic references seem like a good idea, using a data structure based on a hash is the best approach.
Consider using Hash::Util to lock your hashes when you don't want to alter them.
Symbolic references don't work on lexical variables.
Typeglobs don't work on lexical variables.
Use lexical variables.
Don't use symbolic references.
See perlreftut for more on references (symbolic and otherwise).
See perldsc for help using data structures.
See perlmod for more on typeglobs.
See perlsub for more on lexical variables.
Here's an example of using locked hashes to control access to data based on the content of a variable:
use strict;
use warnings;
use Hash::Util qw( lock_hash unlock_hash );
my %data;
lock_hash( %data );
#Altering %data is a fatal exception.
unlock_hash( %data );
%data = (
'bob' => [ 'jerk', 'genius' ],
);
lock_hash( %data );
for my $name (qw/ bob margaret /) {
my $info = $data{$name}; # Fatal error when accessing data for margaret.
print "$name is a\n";
print map "\t$_\n", #$info;
}
All warnings aside, the syntax to use symbolic references should you need to use it (but you won't) is:
use strict;
use warnings;
my $name = 'bob';
our #bob = qw/jerk genius/;
my $qualities;
{ no strict 'refs';
print "$name: ", #$name, "\n";
$qualities = \#$name;
}
print "$name is a #$qualities\n";
Note that the array #bob is declared with our. Symbolic references only work with values in the symbol table. In other words, lexical variables do not work with symbolic references.
Just in case I haven't emphasized this enough, don't use symbolic references.
This is very bad practice. If you ever need to do it, it means you should refactor your code. Perhaps a hash with named keys would be better:
my %data = (
'bob' => [ 'jerk', 'perlfan' ],
);
my $name = 'bob';
print $data{ $name }->[0], "\n";
print $data{ $name }->[1], "\n";
my $stringified = join ' and ', #{ $data{ $name } };
print $stringified, "\n";
See perldoc -q "variable name":
Beginners often think they want to have a variable contain the name of a variable. ...
Short answer: Don't. Long answer: Read the FAQ entry (which is also available on your computer). Longer answer: Read MJD's Why it's stupid to "use a variable as a variable name" (Part 2, Part 3).
You really shouldn't have to do this, see depesz's answer for the alternative, but...
{
no strict 'refs';
print join(q{, }, #$name);
}
Yes, you can use a string scalar as a variable reference, and it looks up the variable by name.
You can also do this with typeglobs, see the Symbol module for an easy way.
Usually when you think you need to use symbolic references, you will be better served by using a hash (associative array).
use strict;
use warnings;
my %user = (
'bob' => [qw' jerk perlfan '],
'mary' => 'had a little lamb',
);
{
for my $name (qw'bob mary susan'){
if( exists $user{$name} ){
if( ref($user{$name}) eq 'ARRAY' ){
print join( ' ', #{$user{$name}} ), "\n";
}else{
print $name, ' ', $user{$name}, "\n";
}
}else{
print "$name not found\n";
}
}
}
Results in:
jerk perlfan
mary had a little lamb
susan not found
If you think you really need to use symbolic references, this is a safer way to do that.
use strict;
use warnings;
# can't use my
our #bob = ("jerk", "perlfan");
our $mary = 'had a little lamb';
{
for my $name (qw'bob mary susan'){
if( exists $::{$name} ){
my $ref = $::{$name};
if( *{$ref}{ARRAY} ){
print join(' ',#$ref), "\n";
}elsif( *{$ref}{SCALAR} ){
print "# $name is not an array, it's a scalar\n";
print $name, ' ', $$ref, "\n";
}
}else{
print "$name not found\n"
}
}
}
Which returns:
jerk perlfan
# mary is not an array, it's a scalar
mary had a little lamb
susan not found
You should notice that it is harder to symbolic references safely, which is why you shouldn't use them.
Unless of course you are an expert doing something advanced.
no strict 'refs';
print "$name is #$name\n";
Symbolic references are worth avoiding, but if you really want to use them, they have the same use syntax as regular references: see http://perlmonks.org/?node=References+quick+reference.