Checking whether a string does not present in hash - perl

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.

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'
];

perl: Use map and foreach at once?

I was wondering if it is possible to make a hash assigning its keys and values at once. Or in general use map and for at one line:
#!/usr/bin/perl
%h = map {$_, $i} qw[a b c] for $i (1..3)
But unfortunatelly not => Number found where operator expected, meant number in the parenthesis. So my question is why am I not able to make double loop by this way? And how otherwise would someone assign hash keys to values (and I dont concern something like $h = {a=>1,b=>2,c=>3} but rather assigning %h = (#keys = #values) ... in other words, how to assign hash by:
2 arrays only (#keys,#values), no scalars
At once (at one line - without block)
Is it even possible in perl?
Populating a hash is simply a matter of assigning a list with alternating keys and values, so you just have to construct the list using the two arrays in an alternating fashion.
use strict;
use warnings;
my #keys = qw(a b c);
my #values = 1..3;
my %h = map { ($keys[$_], $values[$_]) } 0..$#keys;
List::UtilsBy provides a useful abstraction for this in zip_by.
use List::UtilsBy 'zip_by';
my %h = zip_by { #_ } \#keys, \#values;
But actually it's even easier to use slice assignment. Though you technically can't do this in the same statement as the declaration, it's by far the neatest option:
my %h;
#h{#keys} = #values;
Use List::MoreUtils 'zip' or add your own since that module is not a core module:
sub zip(\##){map{($_[0][$_-1],$_[$_])}1..#{$_[0]}}
my %h = zip #keys, #values;
Well, the question is not very clear on 'why?' -- same can be achieved with following code
use strict;
use warnings;
use Data::Dumper;
my $debug = 1;
my %h;
#h{qw(a b c)} = (1..3);
print Dumper(\%h) if $debug;

How to make Perl die after declaring array with non-existent hash keys

I'm declaring an array like this:
my #row = (
$discordances{$assay}{plate_pair},
$assay,
$discordances{$assay}{contigName},
$discordances{$assay}{start},
$discordances{$assay}{test_calls},
$discordances{$assay}{truth_calls}
);
but I want the program to die when I declare #rows using non-existent hash keys
right now, I'm using use warnings FATAL => 'all' & use autodie ':all' but neither of these is catching the error. I have code later which is designed to catch a possible error, but I'm trying to keep this as short as possible for readability and ease of use, so I don't want to use that code to catch something which use autodie ':all' should already be catching.
How can I get Perl to die on such an array declaration?
There is exists. That should let you know whether there is something under the key, also if it is an undef, as in
use strict;
use warnings;
my %the_hash = ( 1 => undef );
# (undef, undef)
my #arr1 = ( $the_hash{1}, $the_hash{2} );
# (undef)
my #arr2 = map { $the_hash{$_} } grep { exists $the_hash{$_} } qw(1 2);
Then you could do something like this:
my #arr = map { $the_hash{$_} } grep { exists $the_hash{$_} or die 'the message' } qw(1 2);
Is that it?
At the expense of an extra pair of actions you can use core Hash::Util to lock the keys
use Hash::Util qw(lock_keys unlock_keys);
my %h = (k1 => 7);
lock_keys %h;
my #ary = (3, $h{nokey}, $h{k1}); # dies; no problem w/o that 'nokey'
unlock_keys %h; # So you can modify it later
This example with a non-existing nokey goes down with
Attempt to access disallowed key 'nokey' in a restricted hash at ...
So remember to unlock keys after the array assignment if you need to be able to change them later.
Note that you may need lock_hash_recurse, to lock deeper keys as well. See docs.
These can be "absorbed" in a do block
my #ary = do {
lock_keys %h;
my #ary_tmp = ( 3, $h{...}, ... );
unlock_keys %h;
#ary_tmp; # returned
};
for some cleanliness and to make sure that unlocking isn't forgotten, but I am not sure whether it helps while it incurs extra expense of a data copy, and of creating an array (only to be discarded).
There is no warning here because it is perfectly legitimate to look up a non-existent hash key and get undef as the result and to have an array element be undef.
If you try to use any of the undef elements as a string or a number, at that point you will get a warning.
If that isn't good enough, you can explicitly check:
die "missing values" if grep ! defined, #row;

Processing array of hashrefs correctly

I have an list of maps in my code.
my #codeList;
push (#codeList, \%map1, \%map2, \%map3);
when I am trying to access it via looping over List index it is not giving me proper map.
what am I missing.
my $Count = #codeList;
for (my $index =0; $index < $Count; $index++)
{
my %map = $codeList[$index];
}
Instead of
my %map = $dbColsList[$dbCount];
you have to use reference as #codeList was populated with them => \%map1
my $map = $dbColsList[$dbCount];
and later use it like $map->{key} as this is array of hashes or hashref structure.
Check perldoc for details.
Alternatively you can dereference hashref and do a shallow copy (changes to %map keys/values won't reflect on \%map1, etc.)
my %map = %{ $dbColsList[$dbCount] };
Your code works fine. i think its your loop ( which you did'nt show us). You can loop through this by dereferencing the hashref (%{ $hashref }):
use strict;
use warnings;
use feature 'say';
my %map1 = (test1 => 'ab');
my %map2 = (test2 => 'ab');
my %map3 = (test2 => 'ab');
my #codeList;
push (#codeList, \%map1, \%map2, \%map3);
for my $hashref (#codeList) {
for my $key (keys %{$hashref}) {
say $key . q{ } . $hashref->{$key};
}
}
EDIT Output:
test1 ab
test2 ab
test2 ab

How can I reference a hash with a variable name?

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.