Issue in hash modification - perl

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";

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}.

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

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

What are anonymous hashes in perl?

$hash = { 'Man' => 'Bill',
'Woman' => 'Mary,
'Dog' => 'Ben'
};
What exactly do Perl's “anonymous hashes” do?
It is a reference to a hash that can be stored in a scalar variable. It is exactly like a regular hash, except that the curly brackets {...} creates a reference to a hash.
Note the usage of different parentheses in these examples:
%hash = ( foo => "bar" ); # regular hash
$hash = { foo => "bar" }; # reference to anonymous (unnamed) hash
$href = \%hash; # reference to named hash %hash
This is useful to be able to do, if you for example want to pass a hash as an argument to a subroutine:
foo(\%hash, $arg1, $arg2);
sub foo {
my ($hash, #args) = #_;
...
}
And it is a way to create a multilevel hash:
my %hash = ( foo => { bar => "baz" } ); # $hash{foo}{bar} is now "baz"
You use an anonymous hash when you need reference to a hash and a named hash is inconvenient or unnecessary. For instance, if you wanted to pass a hash to a subroutine, you could write
my %hash = (a => 1, b => 2);
mysub(\%hash);
but if there is no need to access the hash through its name %hash you could equivalently write
mysub( {a => 1, b => 2} );
This comes in handy wherever you need a reference to a hash, and particularly when you are building nested data structures. Instead of
my %person1 = ( age => 34, position => 'captain' );
my %person2 = ( age => 28, position => 'boatswain' );
my %person3 = ( age => 18, position => 'cabin boy' );
my %crew = (
bill => \%person1,
ben => \%person2,
weed => \%person3,
);
you can write just
my %crew = (
bill => { age => 34, position => 'captain' },
ben => { age => 28, position => 'boatswain' },
weed => { age => 18, position => 'cabin boy' },
);
and to add a member,
$crew{jess} = { age => 4, position => "ship's cat" };
is a lot neater than
my %newperson = ( age => 4, position => "ship's cat" );
$crew{jess} = \%newperson;
and of course, even if a hash is created with a name, if its reference is passed elsewhere then there may be no way of using that original name, so it must be treated as anonymous. For instance in
my $crew_member = $crew{bill}
$crew_member is now effectively a reference to an anonymous hash, regardless of how the data was originally constructed. Even if the data is (in some scope) still accessible as %person1 there is no general way of knowing that, and the data can be accessed only by its reference.
It's quite simple. They allow you to write
push #hashes, { ... };
f(config => { ... });
instead of
my %hash = ( ... );
push #hashes, \%hash;
my %config = ( ... );
f(config => \%config);
(If you want to know the purpose of references, that's another story entirely.)
Anything "anonymous" is a data structure that used in a way where it does not get a name.
Your question has confused everyone else on this page, because your example shows you giving a name to the hash you created, thus it is no longer anonymous.
For example - if you have a subroutine and you want to return a hash, you could write this code:-
return {'hello'=>123};
since it has no name there - it is anonymous. Read on to unwind the extra confusion other people have added on this page by introducing references, which are not the same thing.
This is another anonymous hash (an empty one):
{}
This is an anonymous hash with something in it:
{'foo'=>123}
This is an anonymous (empty) array:
[]
This is an anonymous array with something in it:
['foo',123]
Most of the time when people use these things, they are really trying to magically put them inside of other data structures, without the bother of giving them a waste-of-time temporary name when they do this.
For example - you might want to have a hash in the middle of an array!
#array=(1,2,{foo=>3});
that array has 3 elements - the last element is a hash! ($array[2]->{foo} is 3)
perl -e '#array=(1,2,{foo=>1});use Data::Dumper;print Data::Dumper->Dump([\#array],["\#array"]);'
$#array = [
1,
2,
{
'foo' => 1
}
];
Sometimes you want to don't want to pass around an entire data structure, instead, you just want to use a pointer or reference to the data structure. In perl, you can do this by adding a "\" in front of a variable;
%hashcopy=%myhash; # this duplicates the hash
$myhash{test}=2; # does not affect %hashcopy
$hashpointer=\%myhash; # this gives us a different way to access the same hash
$hashpointer->{test}=2;# changes %myhash
$$hashpointer{test}=2; # identical to above (notice double $$)
If you're crazy, you can even have references to anonymous hashes:
perl -e 'print [],\[],{},\{}'
ARRAY(0x10eed48)REF(0x110b7a8)HASH(0x10eee38)REF(0x110b808)
and sometimes perl is clever enough to know you really meant reference, even when you didn't specifically say so, like my first "return" example:
perl -e 'sub tst{ return {foo=>bar}; }; $var=&tst();use Data::Dumper;print Data::Dumper->Dump([\$var],["\$var"]);'
$var = \{
'foo' => 'bar'
};
or:-
perl -e 'sub tst{ return {foo=>bar}; }; $var=&tst(); print "$$var{foo}\n$var->{foo}\n"'
bar
bar

Perl - reading in multi-line records from config file

I'm trying to read in a multi-line config file with records into a perl hash array
Example Config File:
record_1
phone=5551212
data=1234234
end_record_1
record_2
people_1=bob
people_2=jim
data=1234
end_record_2
record_3
people_1=sue
end_record_3
here's what I'm looking for:
$myData{1}{"phone"} <--- 5551212
$myData{1}{"data"} <--- 1234234
$myData{2}{"people_1"} <--- bob
... etc
What's the best way to read this in? Module? Regex with multi-line match? Brute force? I'm up in the air on where to head next.
Here's one option with your data set:
use strict;
use warnings;
use Data::Dumper;
my %hash;
{
local $/ = '';
while (<DATA>) {
my ($rec) = /record_(\d+)/;
$hash{$rec}{$1} = $2 while /(\S+)=(.+)/g;
}
}
print Dumper \%hash;
__DATA__
record_1
phone=5551212
data=1234234
end_record_1
record_2
people_1=bob
people_2=jim
data=1234
end_record_2
record_3
people_1=sue
end_record_3
Output:
$VAR1 = {
'1' => {
'data' => '1234234',
'phone' => '5551212'
},
'3' => {
'people_1' => 'sue'
},
'2' => {
'people_1' => 'bob',
'data' => '1234',
'people_2' => 'jim'
}
};
Setting local $/ = '' results in an empty line being treated as a "record separator" in your data set, so we can use regexs on those records to grab the information for the hash keys/values.
Hope this helps!
There are a number of modules for this, so the best practice is (as usual) to use them rather than re-invent the wheel.
From the snippet of the config file you posted, it looks like Config::Simple may be the best choice. If you can simplify the config format, then Config::Tiny would be easier to use. If things get more complicated, then you may have to use Config::General.
http://metacpan.org/pod/Config::Tiny
http://metacpan.org/pod/Config::Simple
http://metacpan.org/pod/Config::General
Read it in one line at a time.
When you see a new record, add a new empty associative array to myData and grab a reference to it - this will be your "current record".
Now when you see key/value pairs on a line, you can add that to the current record array (if there is one)
When you see the end of a record, you just clear the reference to the current record.

in perl ,how to use variable value as hash element

I am new to Perl, and can't find the answer to the question in the Learning Perl book.
For example I have a array like:
my #loop=("op1_sel","op2_sel");
and two hash table as:
my %op1_sel=(
"bibuf","000",
"self","101"
);
my %op2_sel=(
"zero","1",
"temp","0"
);
Now I want to use variables in the loop to loop for the hash table for a particular key
for example:
foreach(#loop)
{
print ${$_}{"bibuf"} ;
}
But it seems not working, I know the ${$_} part is wrong, can anyone can tell me how
to fix this ?
Use nested hashes. Like this:
my %op;
# put a hash reference into hash, twice
$op{op1_sel} = \%op1_sel;
$op{op2_sel} = \%op2_sel;
# later ...
foreach (keys %op) {
print "bibuf of $_: $op{$_}->{bibuf}\n";
};
Or, long story short, just
my %op = (
op1_sel => {
foo => 1,
bar => 2,
# ...
},
op2_sel => {
# ...
},
};
The {} construct creates a reference to anonymous hash and is the standard way of handling nested data structures.
See also perldoc perldsc.
You can't refer to lexical (my) variables using the ${$foo} syntax. You could probably make it work if they were package variables, but this would not be the right way to go about it.
The right way to do it is using a nested data structure.
I can see two obvious ways of doing it. You could either make an array of op_sel containing the inner hashes directly, or create a hash of hashes, and then index into that.
So "array of hashes":
my #op_sels = (
{
bibuf => '000',
self => '101',
},
{
zero => '1',
temp => '0',
},
);
for my $op (#op_sels) {
print $$op{bibuf};
}
and "hash of hashes":
my %op_sels = (
1 => {
bibuf => '000',
self => '101',
},
2 => {
zero => '1',
temp => '0',
},
);
for my $op_key (sort keys %op_sels) {
print $op_sels{$op_key}{bibuf};
}
You can use eval for this.
foreach(#loop)
{
eval "\%var = \%$_";
print $var{"bibuf"} ;
}