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.
Related
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
...
}
}
I have a multi-dimensional hash in perl and I would like to change the first key for a chosen value. For example, I have the hash
my %Hash1;
$Hash1{1}{12}=1;
$Hash1{1}{10}=1;
$Hash1{2}{31}=1;
$Hash1{3}{52}=1;
$Hash1{3}{58}=1;
$Hash1{4}{82}=1;
$Hash1{4}{154}=1;
Now I want to replace the value 3 in the first key with the value 300. After this I would get:
$Hash1{1}{12}=1;
$Hash1{1}{10}=1;
$Hash1{2}{31}=1;
$Hash1{300}{52}=1;
$Hash1{300}{58}=1;
$Hash1{4}{82}=1;
$Hash1{4}{154}=1;
I know I could create a new hash by scanning the original hash and doing the following:
my %Hash2;
foreach my $key1 (sort keys %Hash1) {
foreach my $key2 (keys %{ $Hash1{$key1} }) {
if($key1==3){
$Hash2{300}{$key2}=1;
} else {
$Hash2{$key1}{$key2}=1;
}
}
}
But is there a quicker way?
$Hash1{300} = $Hash1{3};
delete $Hash1{3};
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
}
New to Perl. Got syntax errors in accessing key-value pairs from subroutine.
sub displayObj{
my %obj = shift; //the hash. It is a JSON object after decode_json()
my $field = shift; //the key we are searching. It is a string.
my $serialized = "{}"; //Initialization
foreach my $key (keys %obj){
if($key eq $field){
$serialized = $obj[{$field}];
}
}
print "$serialized\n";
}
It is giving me a compilation error in the if block.
I would like to know:
Should I use % or $ in my %obj?
How to access the key-value pair (also a JSON object) and assign it to $serialized using $obj?
I think you're trying to write a subroutine that displays the value of a hash element given its key
But you're missing the basic purpose of hashes: they're content-addressable. That means there's no need to iterate through all the keys of a hash comparing them to the string you're looking for, you can write just $hash{key} and the search will be done for you very quickly using a hashing system (hence the name of the data type). This is just the same as using $array[$i] to access an array element directly instead of looping over all of the indices of the array comparing each one to $i until you find the element you're looking for
If you're really dealing with objects then you shouldn't be accessing their internal data like this anyway. An object will have accessor methods that return the values you're supposed to be using; anything else is part of the internal working of the class and is meant to be private
The syntax error is in this line
$serialized = %obj[{$field}]
where it looks like you're trying to use both a hash key {...} and an array index [...]. That won't work
You don't show how you're calling your subroutine, but I imagine you're passing a reference to a hash, which is a scalar value and must be treated as such inside the subroutine
This program shows a working version of what I think you intended
use strict;
use warnings 'all';
my $obj = {
aa => 1,
cc => 2,
};
displayObj($obj, 'cc');
displayObj($obj, 'bb');
sub displayObj {
my $obj = shift; # The hash. It is a JSON object after decode_json()
my $field = shift; # The key we are searching. It is a string.
my $serialized = '{}'; # Initialization
foreach my $key ( keys %$obj ) {
if ( $key eq $field ) {
$serialized = $obj->{$field};
}
}
print "$serialized\n";
}
output
2
{}
But the loop can be removed altogether as I described, leaving
sub displayObj {
my ($obj, $field) = #_;
my $serialized = $obj->{$field} // '{}';
print "$serialized\n";
}
which produces exactly the same result. In fact there's little point in creating a subroutine to do this; you can write just
print $obj->{bb} // '{}', "\n"
with the same effect
I usually do this way:
sub displayObj{
my $objref = shift;
my %obj = %{$objref};
}
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.