I'm trying to display data from an array of objects obtained using another company's API, but I am getting errors when I attempt to using a foreach loop.
I'm using Dumper to display everything in the array.
print Dumper($object);
Partial output from Dumper:
'enable_dha_thresholds' => 'false',
'members' => [
bless( {
'ipv4addr' => '192.168.1.67',
'name' => 'name.something.com'
}, 'Something::Network::Member' ),
bless( {
'ipv4addr' => '192.168.1.68',
'name' => 'name.something.com'
}, 'Something::Network::Member' )
],
'comment' => 'This is a comment',
I'm trying to extract the "members" which appears to be a double array:
//this works
print $members->enable_dha_thresholds();
//this works
print $members[0][0]->ipv4addr;
//does not work
foreach my $member ($members[0]){
print "IP". $member->ipv4addr()."\n";
}
I receive this error:
Can't call method "ipv4addr" on unblessed reference at ./script.pl line 12.
I'm not sure I entirely understand "blessed" vs "unblessed" in Perl since I am new to the language.
print $members[0][0]->ipv4addr; //this
works
so $members[0] is an array reference.
You have to dereference the array:
foreach my $member ( #{ $members[0] } ){
print "IP". $member->ipv4addr()."\n";
}
The error refering to an "unblessed reference" tells you you aren't using an object; rather you provide an array-reference, which isn't the same :)
HTH,
Paul
It's an issue of "array reference" vs. "array". $members[0] is an array reference; the foreach operator works with arrays (or lists, to be pedantic). You will want to say
foreach my $member ( #{$members[0]} ) { ...
to iterate over the elements that $members[0] refers to.
The syntax is tricky, and you will probably make a few more mistakes along the way with this stuff. The relevant docs to get you up to speed are in perlref (or perlreftut), perllol, and also perldsc and perlobj.
"blessed" by the way, means that a reference "knows" what kind of object it is and what package it should look in to see what methods it can run. When you get an "unblessed reference" warning or error, that usually means you passed something that was not an object somewhere that expected an object -- in this case, $members[0] is the unblessed reference while you intended to pass the blessed references $members[0][0], $members[0][1], etc.
Related
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}.
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,
I'm using Perl 5.12 on Mac 10.5.7. I have a JAR file, that I wish to unzip, and then process a file matching a file pattern. I'm having trouble figuring out how to iterate over the results of unzipping. I have ...
### build an Archive::Extract object ###
my $ae = Archive::Extract->new( archive => $dbJar );
### what if something went wrong?
my $ok = $ae->extract or die $ae->error;
### files from the archive ###
my #files = $ae->files;
for (#files) {
print "file : $_\n";
But there is only one thing returned from the loop -- "ARRAY(0x7fd5c22f7cd8)". I know there are lots of files in the archive, so I'm curious what I"m doing wrong here. - Dave
$ae->files returns an array reference, not an array. Try this:
my $files = $ae->files;
for(#$files) {
print "file : $_\n";
}
From the Perldoc of Archive::Extract:
$ae->files
This is an array ref holding all the paths from the archive. See extract() for details.
It's quite common for methods to return not arrays and hashes, but references to them. It's also quite common for methods to take references to arrays and hashes as arguments. This is because less data has to be passed back and forth between the method and your call.
You can do this:
for my $file ( #{ $ae->files } ) {
print "$file\n";
}
The #{...} dereferences the reference and makes it into a simple array. And yes, you can put method calls that return an array reference in the #{...} like I did.
As already mentioned, a very helpful package is Data::Dumper. This can take a reference and show you the data structure contained therein. It also will tell you if that data structure represents a blessed object which might be a clue that there is a method you can use to pull out the data.
For example, imagine an object called Company. One of the methods might be $company->Employees which returns an array reference to Employee objects. You might not realize this and discover that you get something like ARRAY{0x7fd5c22f7cd8) returned, pushing this through Data::Dumper might help you see the structure:
use Data::Dumper;
use Feature qw(say);
use Company;
[...]
#employee_list = $company->Employees;
# say join "\n", #employee_list; #Doesn't work.
say Dumper #employee_list;
This might print:
$VAR = [
{
FIRST => 'Marion',
LAST => 'Librarian',
POSITION => 'Yes Man',
} Employee,
{
FIRST => 'Charlie',
LAST => 'australopithecus`,
POSITION => 'Cheese Cube Eater'
} Employee,
]
Not only do you see this is an array of hash references, but these are also objects Employee too. Thus, you should use some methods from the Employee class to parse the information you want.
use Feature qw(say);
use Company;
use Employee;
[...]
for my $employee ( #{ $company->Employees } ) {
say "First Name: " . $employee->First;
say "Last Name: " . $employee->Last;
say "Position: " . $employee->Position;
print "\n";
}
Try doing this :
use Data::Dumper;
print Dumper #files;
That way, you will see the content of #files.
If you don't know how to process your data structure, paste the output of Dumper here
I'm writing a small Perl script that goes through an XML file via XML::Simple
my $xml = new XML::Simple;
my $detail= $xml->XMLin($xml_local);
Sometimes, the contents of an element in the XML are empty.
When there is no content in an element in the XML, and I try to print out the contents using:
print $detail->{Parsing}->{Through}->{XML}->{ElementContents}
I get the output:
HASH(0x18948c4)
......or something similar..... the only difference is the chars between the ()'s
I want to test if the content is empty and default the variable to something else - maybe '' or "" - anything but the hash reference/address/whatever that is.
I tried this, but got an error that its not an array reference:
print $detail->{Parsing}->{Through}->{XML}->{ElementContents}[0]
UPDATE
Output of one of the elements using Data::Dumper:
'something' => [
{
'somedetail' => '',
'somedetail' => '',
'somedetail' => 'http://www.google.com'
'somedetail' => 'google',
'somedetail' => '1',
'somedetail' => '01/21/02'
},
How can I test for these '' empty strings using Perl? They are returned as HASH(0x18948c4) unless some filtering is enabled.
The reason it prints HASH(0x18948c4) is because the contents of that value are NOT in fact empty, but a hashref. When you print something, Perl tries to stringify that something, and stringified result of a hash reference is HASH(address) where address is the address of the actual hash.
Print the actual contents of that hashref as follows:
use Data::Dumper;
print Data::Dumper->Dump([$detail->{Parsing}->{Through}->{XML}->{ElementContents}]);
If as you say there are "no contents", it will probably be an empty hashref:
$VAR1 = {};
If so, you can check for it via:
if (ref($detail->{Parsing}->{Through}->{XML}->{ElementContents}) eq ref({})
&& !keys %{ $detail->{Parsing}->{Through}->{XML}->{ElementContents} })
print "No contents, empty hashref";
}
First condition ensures it's a hashref, second, that the hash resulting from its dereference has zero elements as its keys - meaning it's an empty hash being referenced.
However, I seriously doubt it's an empty hash from what I recall about XML::Simple - and doing the Data::Dumper print as shown above will show you HOW to deal with it. You should always print out unknown data structures this way to figure out what to do with them.
E.g., if your Data::Dumper output was:
$VAR1 = {
'a' => 1
};
Then you need to print $detail->{Parsing}->{Through}->{XML}->{ElementContents}->{a}, obviously. Again, be careful to only print something that is a scalar and not an arrayref or hashref, so go down the data structure as much as needed to get to a scalar.
This is a modified version of DVK's answer that worked for me:
if (ref($detail->{Parsing}->{Through}->{XML}->{ElementContents}) eq ref({}))
{
...empty element content...
}
I needed to remove the 2nd condition of the if(condition1 && condition2) statement he gave me.
I am trying to setup a basic error checking system where it will catch shell errors run by a system call. execute_command is a webmin function that runs a system call and then sets an error message to its 4th parameter. I basically call execute_command_error("adduser test"), knowing that I already have a user called test created and based on my predefined arrays, id expect it to print
Unable to add userUnable to
add that user because it already
exists on the system.
but instead I get:
Uhhhhhhhhh? Uhhhhhhhhh?
I have verified that $exe and $return are "adduser" and 1, respectifully.
What am I not understanding about arrays? It seems to ignore the string and or number and just go by the last definition with 3 elements. What is a solution to this, or a better solution?
Here is ths code:
$ErrorMsg['adduser',1,'title'] = "Unable to add user";
$ErrorMsg['adduser',1,'msg'] = "Unable to add that user because it already exists on the system.";
$ErrorMsg['random',2,'duaisdhai'] = "Uhhhhhhhhh?";
sub execute_command_error
{
my $error = "";
my $cmd = $_[0];
$return = execute_command($cmd, undef, undef, \$error)>>8;
if ($error) {
my ($exe) = $cmd =~ m|^(.*?)[ ]|;
$exe_title = $ErrorMsg[$exe,$return,'title'];
$exe_msg = $ErrorMsg[$exe,$return,'msg'];
print $exe_title."<br>";
print $exe_msg ."<br>";
}
}
Update:
I am thinking that I need to use hashes, I have no idea why I thought I could use strings in indices. With that said, little research has led me to something like this:
%ErrorMsgs = ('adduser' => {
'1' => {
'title' => 'Unable to add user',
'msg' => 'Unable to add that user because it already exists on the system.',
},
},
);
Now how would I reference it using a variable? because neither of these work:
$exe_title = $ErrorMsgs{"$exe"}{"$return"}{"title"};
$exe_title = $ErrorMsgs{$exe}{$return}{title};
First, see perldsc for the proper syntax for doing multidimensional structures. Your arrays don't make any sense.
If you had warnings turned on, you would have seen a "Argument isn't numeric" warning to tell you that you can't use strings in any meaningful way in an array index.
But the hash you posted in your update should work fine.
#!/usr/bin/perl
use strict;
use warnings;
## ^^ these things are your friends
my %ErrorMsgs = ('adduser' => {
'1' => {
'title' => 'Unable to add user',
'msg' => 'Unable to add that user because it already exists on the system.',
},
},
);
my $exe = 'adduser';
my $return = 1;
print $ErrorMsgs{$exe}{$return}{title}; # works
If you're not getting the output you expect, it's because there's something wrong with $exe or $return -- they might not be defined in the scope where you're trying to use them. Turning on strict and warnings will help track the issue down.
{ 'key' => 'val' } creates a hash reference, so you dereference before looking up a key.
$exe_title = $ErrorMsgs{$exe}->{$return}->{"title"};
You also don't need to quote $exe or $return, since these already hold strings.
Note that Perl doesn't support multidimensional indices; a multidimensional array is just an array of arrays, so you need to use [] for each index. In scalar context, the comma operator returns the value of the rightmost expression, so the following lines are equivalent:
$ErrorMsg[0,1,2] = "foo";
$ErrorMsg[2] = "foo";
Note that in list context, the comma operator returns a list of values, which gives us slices:
#a=qw(f o o);
#a[3,4,5] = qw(b a r);
print join(',', #a), "\n";
# output: f,o,o,b,a,r
#ErrMsg{qw(title msg)} = ('Unable to add user', 'Unable to add that user because it already exists on the system.')