Can't print the size of a hash of hashes or access element within it - perl

I have a hash %AllData pulling data from database using following code:
while(my $Row = $sth1->fetchrow_hashref())
{
if(defined($Row->{LotID}))
{
$AllData->{$Row->{LotID}}->{$Row->{Name}} = $Row->{Details};
}
}
After I'm done with pulling data, I use print Dumper($AllData); to print out All my data which show like:
$VAR1 = {
'4197359' => {
'Short Description' => 'Rock Picker',
'Lot Number' => '1065',
'Description' => 'Rock Picker'
},
'4194148' => {
'Short Description' => 'Large work bench w/ drawers',
'Lot Number' => '1041',
'Description' => 'Large work bench w/ drawers'
},
'4200944' => {
'Lot Number' => '1084',
'Description' => 'Horse Trailer'
}
}
However, when I try to print out the size of the hash or use foreach to access the hash, it shows 0 size and can't access any element within the hash:
print "Hash Size: ", scalar keys %AllData, "\n";
shows:
Hash Size: 0
What's the cause of my problem?

There is no hash %AllData, and if your program didn't raise an error then you haven't got
use strict;
use warnings;
at the head of you program. This is vital for all Perl programs, especially when you are asking others for help with your code.
The hash you're interested in is the one referenced by $AllData, so you need to use this variable and dereference it. Like this
print "Hash Size: ", scalar keys %$AllData, "\n";

Try accessing scalar keys %$AllData in order to access the hash that the reference.. refers to.
$AllData (what you're passing to Dumper() ) is a reference to a hash (a 'hashref')
%AllData is a different thing to Perl than $AllData. If this hasn't been set yet and perl isn't complaining, you may need to try putting use strict; at the top of your script so that you can be warned of these (and other) types of errors.

Maybe you need to dereference the hash first:
print "Hash Size: ", scalar keys %{ $AllData }, "\n";

should represent a hash ref as $%hash instead of %hash to print

Related

How do I decipher an array of hashes?

I totally got this question wrong. Am using the method from TMDB:
my #results = $search->find(id => 'tt0114694', source => 'imdb_id');
I thought the output was in JSON format, so that's what confused me, which kept me running in circles because I was looking at it all wrong.
Didn't realize the data below, from Dumper, was the actual hashes the I had to go through.
This is where I am running into a wall, So the data below is a hash with five keys. The fifth key, the I want, contains another array. It is that array I cannot read into. I try dereferencing that into a hash, and that is where I fail.
The code I am trying is:
foreach my $narray (#results){
print $narray->{"movie_results"};
my #newarray = $narray->{"movie_results"};
foreach my $otherarray (#newarray){
my %innerhash = $otherarray;
print %innerhash;
print "\n";
}
}
It will print out an array, but I am unable to read the hash in that array.
p.s. I had to format this output as code, or else it came out with no line breaks.
$VAR1 = {
'tv_season_results' => [],
'tv_results' => [],
'person_results' => [],
'tv_episode_results' => [],
'movie_results' => [
{
'adult' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' ),
'vote_average' => '6.8',
'original_title' => 'Tommy Boy',
'vote_count' => 635,
'id' => 11381,
'release_date' => '1995-03-31',
'overview' => 'Party animal Tommy Callahan is a few cans short of a six-pack. But when the family business starts tanking, it\'s up to Tommy and number-cruncher Richard Hayden to save the day.',
'genre_ids' => [
35
],
'title' => 'Tommy Boy',
'video' => $VAR1->{'movie_results'}[0]{'adult'},
'poster_path' => '/g32WbO9nbY5ydpux5hIoiJkLEQi.jpg',
'original_language' => 'en',
'backdrop_path' => '/bZ4diYf7oyDVaRYeWG42Oify2mB.jpg',
'popularity' => '13.945'
}
]
};
You mention that you thought you'd get JSON output, but got something else. The module made a web request for you, received the JSON response, and translated that to a Perl data structure. That Perl version of the JSON is what you see in the dump.
A JSON object turns into a Perl hash, so that's what you see in the top level of the data structure. That's the single thing find returns (more on that in a moment):
Here's what you have, removing the outer foreach loop:
my #newarray = $narray->{"movie_results"};
foreach my $otherarray (#newarray){
my %innerhash = $otherarray;
print %innerhash;
print "\n";
}
The value in $narray->{"movie_results"} is an array reference. All references are scalars, and those scalars point to some data structure. When you assign that scalar to an array, you just end up with a one element array that's the same reference. Instead, you can
my $movie_results = $narray->{"movie_results"};
You then dereference that reference to treat it as an array:
foreach my $result ( #$movie_results ){ ... }
Or, the v5.24 postfix dereferencing way that I find slightly more pleasing since it reads better, especially when you skip the intermediate variable:
foreach my $result ( $movie_results->#* ){ ... }
foreach my $result ( $narray->{"movie_results"}->#* ){ ... }
That thing in $result is another hash reference.
References and data structures are about half of the content of Intermediate Perl, but there is also the Perl data structures cookbook (perldsc).
Improving your question a bit
You can help us a lot by showing us a complete, working demonstration of your problem. Here's what I cobbled together:
use v5.10;
use TMDB;
use Data::Dumper;
my $tmdb = TMDB->new( apikey => $ENV{TMDB_API_KEY} );
my #results = $tmdb->search->find(
id => 'tt0114694',
source => 'imdb_id'
);
say Dumper( \#results );
There was a question about the results of find. The documentation example shows it returning a list (well, the result being assigned to a named array, which implies that), but there's not actual documentation for find. It returns the decoded JSON from the response. Assigning it to a scalar (which will be a reference) works just fine too:
my $result = $tmdb->search->find(
id => 'tt0114694',
source => 'imdb_id'
);
say Dumper( $results );
The return value comes from TMDB::Sesssion::talk(), which is just this (or the empty list):
return $self->json->decode(
Encode::decode( 'utf-8-strict', $response->{content} ) );
That's not a big deal. That just means you don't need the outer foreach. That's not on you because the example in the docs tells you to do exactly what you did.
Now a better program
Putting all that together, here's a simple program pared down to just what you need:
use v5.10;
use TMDB;
my $tmdb = TMDB->new( apikey => $ENV{TMDB_API_KEY} );
my $result = $tmdb->search->find(
id => 'tt0114694',
source => 'imdb_id'
);
foreach my $item ( $result->{movie_results}->#* ) {
say "Title: $item->{title}";
}
Ref aliasing
There's an experimental feature called ref aliasing that lets you assign a reference to a reference of a named variable. It's an alias, so you can access and change the same data, just with a named variable. Something this is handy when you don't like
use v5.10;
use TMDB;
use experimental qw(refaliasing);
my $tmdb = TMDB->new( apikey => $ENV{TMDB_API_KEY} );
# response is a hash ref, so ref alias to a named hash
\my %result = $tmdb->search->find(
id => 'tt0114694',
source => 'imdb_id'
);
# the part you want is an array ref, so alias that
\my #movie_results = $result{movie_results};
# each item in the array is a hash ref, so alias those too
foreach \my %item ( #movie_results ) {
say "Title: $item{title}";
}
When dealing with reference, use the same syntax as if you weren't, but replace the name of the variable with a block that returns the reference.
%NAME -> %{ $ref } Or just %$ref
$NAME{...} -> ${ $ref }{...} Although $ref->{...} easier to read.
#NAME -> #{ $ref } Or just #$ref
$NAME[...] -> ${ $ref }[...] Although $ref->[...] easier to read.
Let's give $VAR a better name,
my $response = $VAR1;
This means you want
my $results = $response->{movie_results};
for my $result (#$results) {
for my $key (keys(%$result)) {
say "$key: $result->{$key}";
}
}
See
perlreftut
Perl Dereferencing Syntax
%newhash{$newkey} should be $newhash{$newkey}.

Accessing Hash of hash of Array in Perl

I have the following data I wish to access:
$data1=
{'Family' => {
'House' => [
{
'Id' => '1111',
'Name' => 'DFG'
},
{
'Id' => '211',
'Name' => 'ABC'
}
]
}
}
I want to access the each Name field value. I am using this code:
foreach(keys%$data1) {
if(ref($data1->{$_}) eq 'HASH') {
foreach my $inner_key (keys%{$data1->{$_}}) {
print "Key:$inner_key and value:$data1->{$_}->{$inner_key}\n";
}
}
else {
print "Key: $_ and Value: $data1->{$_}\n"
}
}
It prints Key:House and value:ARRAY(OXXX).
I know I am doing something wrong. Since the data in 'House' is an array of hashes, I even tried accessing through $data1->{$_}->{$inner_key}[0]. What is wrong in the code???
You have to dereference array for foreach loop first, and then dereference hashref to reach "Name" values.
print "Key:$inner_key and value:$_->{Name}\n"
for #{$data1->{$_}->{$inner_key}};
You should read perlref first to learn how to create and use references.
Here is a demonstration:
#!/usr/bin/perl
use strict;
use warnings;
my $data1=
{'Family' => {
'House' => [
{
'Id' => '1111',
'Name' => 'DFG'
},
{
'Id' => '211',
'Name' => 'ABC'
}
]
}
};
while (my ($key1, $val1) = each %$data1) {
print "\$key1 = $key1\n";
while (my ($key2, $val2) = each %$val1) {
print "\t\$key2 = $key2\n";
foreach my $val3 (#$val2) {
while (my ($key4, $val4) = each %$val3) {
print "\t\t\$key4 = $key4 => $val4\n";
}
print "\n";
}
}
}
[Edit I typed too slowly while answering, so this response bascially duplicates #mpapec's below - I will leave the references here and you can vote me up for those ;-) but do not accept my response as the answer].
Try something like the following to see if it works:
for $inner_hash (#{ $data1->{Family}{House} }) {
say "Name: $inner_hash->{Name}"
}
since you need to get the inner hashes' values from inside the elements of the array (that is what value:ARRAY(OXXX) is telling you).
You can use perldoc to look at the perldata, perlref, perlreftut and perldsc PODs to learn more about data structures and dereferencing. If keeping your data structure in mind while you are writing code gets to be too hard to do, it may mean you need to simplify things: either the data itself or by writing sub's to make it easier to access, or making use some of the excellent utility modules from CPAN.
There's also some good perl data structure related tutorials out there. The POD/perldoc documentation that ships with perl (along with Chapter 9 of Programming Perl) is the canonical reference, but you might browse these nodes from perlmonks:
References quick reference
Referencing in advanced data structures
Visualizing perl data structures
Perlmonks Hash Tutorial
NB Above I'm using the perlcritic and Perl Best Practices style of dereferencing: e.g.: #{ $data1->{Family}{House} } so the syntax reminds me that the inner hashes (or inner-inner?) are inside an array. There's a cool new way of dereferencing being introduced in perl 5.20 called postfix dereferencing which will be great, but you can't go wrong following the recommendations of PBP.
"Until you start thinking in terms of hashes, you aren't really thinking in Perl." -- Larry Wall
Cheers,

Issue in hash modification

I am printing a hash [ print Dumper($myhash); ], it is as below :
$VAR1= {
'context_verdict' => 'Failed',
'logfile' => 'abc',
'Block_000' => {
'Element_0032' => {
'e_verdict' => 'FAILED',
'e_name' => 'Element_0032',
'e_log' => 'This is really bad...',
'e_ref' => 'Good'
}
}
Now I want to change the value of logfile from abc to def. how to achieve this ?
I wrote
$myhash{'$VAR1'}->{'logfile'}="def";
But it does not works!! It is still the "abc".
Try this one:
$myhash->{'logfile'}="def";
Data::Dumper names your variable as $VAR1, this is not an entry in your hash.
First of all, always use use strict; use warnings;.
You want
$VAR1->{'logfile'} = "def";
If you obtained the dump using Dumper(\%myhash),
$myhash{'logfile'} = "def";
If you obtained the dump using Dumper($myhash),
$myhash->{'logfile'} = "def";
$myhash holds a reference to a hash, so you need to dereference it to access the hash. That's what -> is doing.
Data::Dumper helps to analyse a huge hash and the values will be named $VAR in the output.
Answer to your question is:
You can set the value as
$myhash->{'logfile'}="def";

Using a : in a variable name in perl

I'm really new to perl and have an issue where I load in XML using XML::Simple and I have the tag names as hash names. I want to get at the hash stored with the name "xsd:schema" but obviously $xsd:schema doesn't work. I've spent ages googling it and cant find how to do it.
How do I get at that hash so I can find out the key values?
Edit:
Sorry I didn't explain myself very well. I want to find out the keys and values of those keys in a hash several levels deep but the name xsd:schema is causing a syntax error:
foreach my $attributes (keys %{ $data{$xsd:schema}{$xsd:element}}){
print "$attributes : ${$data}{$xsd:schema}{$xsd:element}{$attributes}\n";
}
Edit 2:
Here is how I did it.
$schemaData = $data->{'xsd:schema'}->{'xsd:element'}->{'xsd:complexType'}->{'xsd:sequence'}->{'xsd:element'}->{'xsd:complexType'}->{'xsd:sequence'}->{'xsd:element'};
print Dumper($schemaData);
foreach my $fieldName (keys %{ $schemaData}){
$fieldType = $schemaData->{$fieldName}->{'type'};
print "$fieldType\n";
}
Use Data::Dumper to visualize complex data structures.
XML::Simple does not create new variables, it creates a reference. See Perl Data Structures Cookbook.
use Data::Dumper;
use XML::Simple;
my $x = XMLin(q(<r xmlns:xsd="xsd"><xsd:schema atr="a">a</xsd:schema></r>));
print Dumper $x;
print $x->{'xsd:schema'}{content};
Output:
$VAR1 = {
'xsd:schema' => {
'content' => 'a',
'atr' => 'a'
},
'xmlns:xsd' => 'xsd'
};
a

Perl - Return an array of hashes

i have an array of hashes to be returned.
before returning the array i cross checked it. it was working fine.
but after returning the array of hashess to the calling sub, i am not able to read it.
plz find the below code for referrence.. and do let me know how to read/ return an array of hashes
Thanks... :)
#!/usr/bin/perl
use strict;
use warnings;
# Subroutine prototypes
sub get_two_arrays();
my #one=();
#one = get_array_Hashes();
print "\n First: #one->{Version}\n"; // Printing the return array of hashes
sub get_array_Hashes() {
my #dotNetHashArray =();
my $dotNetHash1 = {Version => "Test-1 Version", SP => "installedSp", Build => "installedBuild"};
push #dotNetHashArray, $dotNetHash1;
my $dotNetHash2 = {Version => "Test-2 Version", SP => "installedSp", Build => "installedBuild"};
push #dotNetHashArray, $dotNetHash2;
my $dotNetHash3 = {Version => "Test-3 Version", SP => "installedSp", Build => "installedBuild"};
push #dotNetHashArray, $dotNetHash3;
print "Test Array of hashes before return";
for(my $i=0; $i<#dotNetHashArray; $i++)
{
print("\n Hash Value : ".$dotNetHashArray[$i]->{Version});
}
return \#dotNetHashArray
}
Perl isn't C, and prototypes are meant for something very different and special. If you don't know what niche purpose they serve then never use them
Similarly there is no reason to pre-declare a subroutine before calling it. As long as you don't use prototypes Perl will do the right thing
There is also no reason to initialise arrays when you declare them if you want them empty. That is what Perl does by default
People familar with Perl would thank you for using lower-case and underscore identifiers for variables and subroutines. Camel case is usually reserved for package names
As others have said, you are returning a reference to an array. But instead of dereferencing the return value it is probably better if you keep it as a reference and use it as such. The only change necessary is to iterate over the array that is returned
Here is a more canonical form of your program which I hope will help
use strict;
use warnings;
my $one = get_array_Hashes();
print "\nArray of hashes after return\n";
print "First: $_->{Version}\n" for #$one;
sub get_array_Hashes {
my #dotnet_hash_array;
my $dotnet_hash1 = {
Version => "Test-1 Version",
SP => "installedSp",
Build => "installedBuild"
};
push #dotnet_hash_array, $dotnet_hash1;
my $dotnet_hash2 = {
Version => "Test-2 Version",
SP => "installedSp",
Build => "installedBuild"
};
push #dotnet_hash_array, $dotnet_hash2;
my $dotnet_hash3 = {
Version => "Test-3 Version",
SP => "installedSp",
Build => "installedBuild"
};
push #dotnet_hash_array, $dotnet_hash3;
print "Test Array of hashes before return\n";
for my $i (0 .. $#dotnet_hash_array) {
print "Hash Value : $dotnet_hash_array[$i]->{Version}\n";
}
return \#dotnet_hash_array
}
output
Test Array of hashes before return
Hash Value : Test-1 Version
Hash Value : Test-2 Version
Hash Value : Test-3 Version
Array of hashes after return
First: Test-1 Version
First: Test-2 Version
First: Test-3 Version
You are returning a reference to an array:
return \#dotNetHashArray
you have to
#one = #{ get_array_Hashes() };
to dereference it.
In addition
the // comment will not work (use #)
usually you don't need to use prototypes in Perl (see Why are Perl 5's function prototypes bad?)
you will need a loop also after the return to print out the values
you don't need a cursor variable to iterate over arrays in Perl
for my $item (#dotNetHashArray) {
print "\n Hash Value: $item->{Version}";
}
if you need to have the \n at the beginning of your prints you are a missing a \n after the loop
You will end up with:
#!/usr/bin/perl
use strict;
use warnings;
# do not use function prototypes
# perl subroutines are usually all lowercase (no camel-case)
sub get_array_hashes {
my #dot_net_hash_array = ();
# actually you don't need to create a local variable for each item you push
push #dot_net_hash_array, {
# avoid unncessary string interpolation (use ' if no variables in the string have to be interpolated)
version => 'Test-1 Version',
sp => 'installedSp',
build => 'installedBuild'
};
push #dot_net_hash_array,
{
version => 'Test-2 Version',
sp => 'installedSp',
build => 'installedBuild'
};
push #dot_net_hash_array,
{
version => 'Test-3 Version',
sp => 'installedSp',
build => 'installedBuild'
};
print "Test Array of hashes before return\n";
for my $item (#dot_net_hash_array) {
print "Hash Value : $item->{version}\n";
}
return \#dot_net_hash_array;
}
my #one = #{ get_array_hashes() };
# Use # for comments
# Printing the return array of hashes
print "Test Array of hashes after return\n";
for my $item (#one) {
print "Hash Value : $item->{version}\n";
}