How to print key value of hash reference in perl? - perl

I am using a perl module called Nmap::Parser and stuggling with how to handle hash references, I am new to these. A call to one of the functions in this module is instructed like this:
A basic call to scripts() returns a list of the names of the NSE scripts run for this port. If $name is given, it returns a reference to a hash with "output" and "contents" keys for the script with that name, or undef if that script was not run. The value of the "output" key is the text output of the script. The value of the "contents" key is a data structure based on the XML output of the NSE script.
How am I supposed to access these key values, I tried the following:
my $script = $service->scripts();
foreach my $key ( keys %$script) {
print $key, " => ", $script->{$key},"\n";
foreach my $seckey ( keys %$key ) {
print $seckey, " => ", $key->{$seckey},"\n";
}
}
And this was my output:
ldap-rootdse

keys returns strings, so $key is a string, so keys %$key and $key->{$seckey} are wrong.
If you have a HoH, you want
for my $key ( keys %$script ) {
my $val = $script->{$key};
for my $inner_key ( keys %$val ) { # %$val, not %$key
my $inner_val = $val->{ $inner_key }; # $val, not $key
...
}
}

Related

perl using subroutine as argument of a foreach

my %number_words = hash_a_file($_);
foreach my $key ( keys %number_words ) {
++$word_list{$key};
}
This is working but I would like to avoid using the intermediate variable
like this
foreach my $key ( keys hash_a_file($_) ) {
++$word_list{$key};
}
I tried to use refs but still failed. Any way to do this ? Thanks !
The thing is, a subroutine doesn't return a hash. It returns a list. In your original code, it only becomes a hash when you store it in a hash variable.
But there are other ways to create a hash from a list. You can create an anonymous hash and then de-reference it. It's ugly, but it works.
# Inner braces create an anonymous hash.
# Outer braces de-reference that into a "real" hash
foreach my $key ( keys %{ { hash_a_file($_) } } ) {
++$word_list{$key};
}
Update: To back up Borodin's comment, I should add that if this code was presented to me in a code review, I'd suggest rewriting it to use an explicit hash variable as your original code does.
Return a hashref, so that you can form a valid argument for keys (instead of a list)
sub hash_a_file { return { a => 1, b => 2 } }
foreach my $key ( keys %{ hash_a_file() } ) {
say $key
}

Which key from which hash is this expression accessing?

I am tracing a Perl script and I cannot understand what the following expression is supposed to do:
keys %{ $data->{net_assets_detail}->{$port}->{$manager} }
I am trying to understand which key from which hash we are trying to access — from data, port or manager?
Below is the complete code:
foreach my $port ( keys %{$data->{net_assets_detail} } ) {
foreach my $manager (keys %{ $data->{net_assets_detail}->{$port} } ) {
my $fund_value = MOA::CLSUtils::get_manager_fund_value( $port, $manager, $args->{end_date} );
$fund_value ||=0;
my $net_asset_value = $data->{net_assets_manager}->{$port}->{$manager};
$net_asset_value ||=0;
foreach my $tran_type (keys %{ $data->{net_assets_detail}->{$port}->{$manager} } ) {
my $value = $data->{net_assets_detail}->{$port}->{$manager}->{$tran_type};
print OUT"$port\t";
print OUT"$manager\t";
print OUT"$tran_type\t";
print OUT"$value\n";
}
}
}
}
Let's look at the line part by part.
%{ $data->{net_assets_detail}->{$port}->{$manager} }
There is a hashref called $data.
Inside, there are some keys. One of them is net_assets_detail
Inside is a hashref
Inside, there are some keys. One of them is the value of $port
Inside is a hashref
Inside, there are some keys. One of them is the value of $manager
The %{ ... } is dereferencing the hashref, so built-ins that expect a hash can work on it.
The keys takes all the keys of the hash (which was dereferenced from the long thingy) and returns them as a list. The foreach iterates over that list, and puts each key into the lexical variable $tram_type that is available in the body of the loop.

Not able to print value of this complex datastructure in perl

Question explained as comments in the code:
I have a following piece of code in which i am trying to make a hash in which the key itself is a reference to some other array.
my #arr1=(1,2,3,4,5,6,7,8,9,10);
my #arr2=(1001,1002,1003);
$FILES_1=\#arr1;
$num1=2;
$FILES_2=\#arr2;
$num2=4;
#FILES=($FILES_1, $FILES_2);
#NUMS=($num1,$num2);
fun (\#FILES,\#NUMS);
sub fun{
my ($rFILES,$rNUMS) = #_;
print "\n --${$rFILES}[0]->[2] \n "; # This is same as below
print "\n --$rFILES->[0]->[2] \n "; # This is same as above
my %hash=();
$hash{$rFILES->[0]} = $rNUMS->[0];
my $test = $rFILES->[0];
print "\nTEST : $test->[1]";
my #key = keys %hash;
print "\nKey 1 = $key[0]"; # This prints scalar value as below
print "\ntest = $test "; # This prints scalar value as above
print "\nKey 1->[1] = ${$key[0]}->[1]"; #PROBLEM : THIS DOESNT PRINT SAME AS BELOW
print "\ntest->[1] = $test->[1] "; #THIS PRINTS AS EXPECTED.
}
Output:
--3
--3
TEST : 2
Key 1 = ARRAY(0x1bbb540)
test = ARRAY(0x1bbb540)
Key 1->[1] =
test->[1] = 2
Are we not supposed to keep a key of a hash as reference to some array? Why is the value "2" not printed?
A hash key is always a string, you can't store a reference in there. I can't think of a reason you'd want this either.
You should really always use strict and use warnings. If you added those and declared your variables with my properly, you'd have gotten a warning:
Can't use string ("ARRAY(0x85e628)") as a SCALAR ref while "strict refs" in use
The syntax you're using:
${$key[0]}
says $key[0] is a reference to a scalar. It isn't, it's a string with the address of what the reference used to be, as you can't use a reference as a hash key they become strings.
Update:
You probably want something like this instead:
my #filegroups = (
{ number => 1, files => ['file1','file2'] },
{ number => 2, files => ['file3'] },
);
Accessed as:
foreach my $filegroup ( #$filegroups ) {
print "my number is $filegroup->{number}\n";
print " and I have ".scalar( #{ $filegroup->{ files } } )." files\n";
}
If you need extra speed to access the structure by group number (don't bother unless you have hundreds of groups or thousands and thousands of accesses), index them:
my %filegroupindexes = map { $_->{ number } => $_ } values #$filegroups;
Then you can get to the group like this:
print "there are ".scalar( #{ $filegroupindexes{ 1 }->{ files } } )." files in group 1\n";
As a last hint, for printing complex data structures Data::Printer is my favourite:
use Data::Printer colored => 1;
p( #filegroups );
You need Tie::RefHash if you want to use references as hash keys.

How to print out a hash that is named like a regular variable

I've been thrust into supporting a perl cgi program, and I have very little functional perl experience.
There is a method that takes $args and I want to see what the $args are.
Here's how I tried first:
print( "=== DEBUG 2 === Creating session with the following args: ", $args, "\n" );
Which prints:
=== DEBUG 2 === Creating session with the following args: HASH(0x2b462cc7c880)
So it's a hash, ah, well. A little googling and I try this:
my $counter = 1;
print( "=== DEBUG === Creating session with the following args: \n" );
foreach (keys $args) {
print "$_ : $args{$_}\n";
}
This causes the entire program to crash with no helpful error message. I guess that $args cannot be used with keys.
How can I dump the contents of $args to print ?
Note: Trying to use Data Dumper also causes the entire program to crash with no error message.
$args is not a hash, it's a hash reference. You need to dereference it first to access the underlying hash.
foreach (keys %$args) {
# You could use this:
# print "$_: ${$args}{$_}\n";
# but the -> operator is a little more readable.
print "$_: $args->{$_}\n";
}
The each operator lets you iterate over a hash while providing a name for each key and value:
while (my ($key, $value) = each %$args) {
print "$key: $value\n";
}
Try this way:
foreach (keys %{$args}) {
print "$_ : ".$args->{$_}."\n";
}
Probably $args is not a hash, but a reference to a hash.
$args seems to be a hash reference.
To print the entire hash:
print %$args;
To print it key by key:
print "$_ $args->{$_}\n" foreach (keys %$args);

Is this the correct way to build a Perl hash that utilizes arrays?

This is the first time I have manipulated hashes and arrays in this way -- and it is working. Basically, for every key there are multiple values that I want to record and then print out in the form "key --> value --> value --> val..."
My code is as follows. I am surprised that it works, so concerned that it works "by mistake". Is this the correct way to accomplish this task, or is there a more efficient or appropriate method?
while ($source =~ m/(regex)/g) { #Get all key names from source
$listkey = $1; #Set current list key to the current regex result.
$list{$listkey} = ++$i unless $list{$listkey}; #Add the key to the hash unless it already exists.
$list{$listkey} = [] unless exists $list{$listkey}; #Add an array for the hash unless the hash already exists.
while ($loopcount==0) {
if ($ifcount==0) {
$listvalue=result_of_some_function_using_list_key; #Get the first list value by using the list key.
$ifcount++; #Increment so we only get the first list value once.
} else {
$listvalue=result_of_some_function_using_list_value; #Update the list value by using the last list value.
}
if ($listvalue) { #If the function returned a value...
push #{$list{$listkey}}, $listvalue; #...then add the value to the hash array for the key.
} else { #There are no more values and we need a new key.
$listkey=0; #Reset variable.
$listvalue=0; #Reset variable.
$loopcount++; #Increment loop counter to exit loop.
}
}
$ifcount=0; #Reset count variable so the next listvalue can be generated from the new key.
$loopcount=0; #Reset count variable so another loop can begin for a new key.
}
foreach $listkey (keys %list) { #For each key in the hash.
print "$listkey --> "; #Print the key.
#values = #{$list{$listkey}}; #Reference the arrays of the hash.
print join ' --> ', #values; #Print the values.
print "\n"; #Print new line.
}
The following code does the same as your code, without the unnecessary steps.
while ($source =~ m/(regex)/g) { # Get all key names from source
$listkey = $1; # Grab current regex result.
$listvalue = result_of_some_function_using_list_key;
while ($listvalue) {
push #{$list{$listkey}}, $listvalue;
$listvalue = result_of_some_function_using_list_value;
}
$listkey = 0; # Reset variable.
$domain = 0; # Reset variable.
}
However, as others have commented, global variables should be avoided in most cases. Instead, the list key and list value should be lexically scoped with my(), and the functions for generating list values should take one or more parameters (domain, list key and/or list value) as input.
The lines
$list{$listkey} = ++$i unless $list{$listkey};
$list{$listkey} = [] unless exists $list{$listkey};
in your original code aren't needed, it is sufficient with push #{ $list{$key} }, $value to initialize an entry.
The code above has many unnecessary steps. Perl is a very expressive language, and allows logic like this to be expressed very simply:
# uncomment for some sample data
# sub function {"#_" !~ /^\[{3}/ and "[#_]"}
# my $source = 'one two three';
my %list;
while ($source =~ m/(\S+)/g) {
my $key = $1;
my $value = function($key);
while ($value) {
push #{ $list{$key} }, $value;
$value = function($value)
}
}
for my $key (keys %list) {
print join(' --> ' => $key, #{$list{$key}}), "\n";
}
Nope! If this works, it's definitely "by mistake". But it's also obvious that this isn't your real code and that you added several more mistakes in "translating" it to an example, so it's hard to judge exactly what the intent was, but going from the skeleton of your program, it looks like it should be something like:
my %result;
while ($source =~ m/(regex)/g) {
my $key = $1;
my $value = mangle($key);
while ($value) {
push #{ $results{$key} }, $value;
$value = frob($value);
}
}
and no more. Your attempts to initialize the hash aren't doing what you think they are (and aren't necessary), your while loop as written isn't a good idea at all, and neither are all the global variables.