Why does Data::Dumper show values that link to other values? - perl

using Data Dumper after parsing some JSON data, I got something like this:
$VAR1 = {
param1 => 'foo',
param2 => $VAR1->{param1}
};
Do I get it right, that param2 is a linked to param1 value?
What is this called? Dynamic hash?
Thanks in Advance, Steve

No need to be confused, the value of param2 is simply a reference that was encountered before in the structure, so Data::Dumper by default shows it as the reference it is. You can set $Data::Dumper::Deepcopy = 1; and have Data::Dumper print the actual values instead if you need it for something.
E.g.
my $foo = 'foo';
my $test = {
param1 => \$foo,
param2 => \$foo
};
print Dumper($test);
will print out
$VAR1 = {
'param2' => \'foo',
'param1' => $VAR1->{'param2'}
};
But if you start with something like:
use Data::Dumper;
$Data::Dumper::Deepcopy = 1;
Your output will be:
$VAR1 = {
'param1' => \'foo',
'param2' => \'foo'
};
The default behavior is more useful for visual inspection.

Related

Array of Hashrefs: How to access based on hashref "column" values

I have an array of hashrefs built from a database using fethrow_hashref(). The data structure is built like so:
while (my $ref = $sth->fetchrow_hashref()) {
push #lines, $ref;
}
I sort the data in the query by program name ascending, so all of the references in the array are still in alphabetical order. Then, I go through each hash and find the value that is numerically equal to a '1'. I then take the caolumn name, and store it to compare to the rest of the hashrefs with that program name to ensure they all have a '1' in the same column.
my $pgm = "";
my $met_lvl = "";
my #devs = ();
my %errors = ();
my $error = "";
foreach my $line_ref (#lines) {
if ($pgm ne $line_ref->{"PROGRAM"}) {
if (#devs && $error) {
# print " Different number metal layers for $pgm: #devs \n";
$error = "";
}
#devs = ();
$pgm = $line_ref->{"PROGRAM"};
($met_lvl) = grep { $line_ref->{$_} == 1 } keys(%$line_ref);
push #devs, $line_ref->{"DEVICE"};
} elsif ($pgm eq $line_ref->{"PROGRAM"}) {
push #devs, $line_ref->{"DEVICE"};
my ($met_chk ) = grep { $line_ref->{$_} == 1 } keys(%$line_ref);
if ($met_chk ne $met_lvl) {
$errors{$line_ref->{"PROGRAM"}} = $line_ref->{"PROGRAM"};
$error = "YUP";
}
}
}
I'd like to be able to access the hashrefs individually, based on matching column names from the database. How can I access the hashrefs with "TEST" values for "PROGRAM" keys? I used Data::Dumper to provide an example of a few of the hashrefs I'd like to access based on "PROGRAM" value:
'PLM' => undef,
'SLM' => undef,
'QLM' => undef,
'DEVICE' => 'DEV1',
'TLM' => '1',
'DLM' => undef,
'ROUTING' => 'NORMAL',
'PROGRAM' => 'TEST'
};
$VAR455 = {
'PLM' => undef,
'SLM' => undef,
'QLM' => undef,
'DEVICE' => 'DEV2',
'TLM' => '1',
'DLM' => undef,
'ROUTING' => 'NORMAL',
'PROGRAM' => 'TEST'
};
$VAR456 = {
'PLM' => undef,
'SLM' => undef,
'QLM' => undef,
'DEVICE' => 'DEV3',
'TLM' => '1',
'DLM' => undef,
'ROUTING' => 'NON_STANDARD',
'PROGRAM' => 'EXP'
};
$VAR457 = {
'PLM' => undef,
'SLM' => undef,
'QLM' => undef,
'DEVICE' => 'DEV4',
'TLM' => '1',
'DLM' => undef,
'ROUTING' => 'NORMAL',
'PROGRAM' => 'FINAL'
};
I'd like to be able to access key values for the hashrefs which contain the same program name. I cannot even begin to figure out what type of operation to use for this. I assume map is the correct way to do it, but dereferencing the "PROGAM" value for each element (hashref) in the array is beyond the scope of my understanding. I hope I was able to define the problem well enough for you guys to be able to help.
Edit: The impetus for wanting to access hashrefs with the same 'PROGRAM" value is to be able to provide an output of selected values to print to a logfile. So, after I compare and find differences between those hashrefs with the same "PROGRAM" value, I want to access them all again, and print out the desired column values to the lofgile.
Looks like you need to exrtact subsets of your data (hashrefs) with the same PROGRAM name.
Can preprocess your data to build a hash with those names as keys, and arrayrefs (with suitable hashrefs) as values. Then process those groups one at a time.
use warnings;
use strict;
use feature 'say';
use Data::Dumper; # to print complex data below
... populate #lines with hashrefs as in the question or copy-paste a sample
# Build hash: ( TEST => [ hashrefs w/ TEST ], EXP => [ hashrefs w/ EXP ], ... )
my %prog_subset;
for my $hr (#lines) {
push #{ $prog_subset{$hr->{PROGRAM}} }, $hr;
# Or, using "postfix dereferencing" (stable from v5.24)
# push $prog_subset{$hr->{PROGRAM}}->#*, $hr;
}
foreach my $prog (keys %prog_subset) {
say "\nProcess hashrefs with PROGRAM being $prog";
foreach my $hr (#{ $prog_subset{$prog} }) {
say Dumper $hr;
}
}
(See postfix dereference)
Now %prog_subset contains keys TEST, EXP, FINAL (and whatever other PROGRAM names are in data), each having for value an arrayref of all hashrefs which have that PROGRAM name.
There are other ways, and there are libraries that can be leveraged, but this should do it.
OK! I found an example of this with the google machine. I replaced #lines = (); with $lines = [];. This allowed me to change the grep statement to (#found) = grep { $pgm eq $_->{PROGRAM} } #$lines;. Now the returned array is a list of the hashrefs that share the program name I'm looking for. Thanks for the help #zdim!

Data::Dumper returned hash with a slash

So I have a line of perl code that reads like this:
my $stored_value = $foo->some_function($argument);
When I do a Dumper on it:
warn Dumper $stored_value
I receive this as a result.
$VAR1 = \{
'foo' => 'abc',
'bar' => '123'
};
Now, I've seen results like this:
warn Dumper $another_hash;
$VAR1 = {
'foo' => 'bar',
'baz' => 'quux'
};
And if I wanted to get say, foo's value, I'd type in something like this:
warn Dumper $another_hash->{'foo'};
To get this as a result.
$VAR1 = 'bar';
Originally, I couldn't find anything through my Google searches, but just now, I made a little test script to play around with what I saw, and I found this out
#!/usr/bin/perl
#
use strict;
use warnings;
use Data::Dumper;
sub test {
my $brother = {'Ted'};
$brother->{'Ted'} = 'brother';
return \$brother;
}
my $blah= test();
my $blah = ${$blah};
print Dumper $blah->{'Ted'};
print "\n";
Here are my results:
$VAR1 = 'brother';
I wanted to share what I had found incase someone else ran into the same thing, but what exactly did I see?
I saw how to do this in http://perldoc.perl.org/perlref.html#Using-References, but I just wanted some clarification on it.
I'm not sure what your question is, but your output shows that $stored_value is a reference to a scalar, which, in turn, is a reference to a hash.
It is rarely useful to keep references to scalars so this may indicate a bug.
This short program shows how the value could have been created
use strict;
use warnings;
use Data::Dumper;
my $href = {
foo => 'abc',
bar => '123',
};
my $href_ref = \$href;
print Dumper $href_ref;
output
$VAR1 = \{
'bar' => '123',
'foo' => 'abc'
};
And, by the way, it is usually more useful to use Data::Dump in preference to Data::Dumper

Perl Hash of Hashes Storing References To Within - Can't use string ("") as a HASH ref

Alright I have a function which generates a hash tree that dumper prints out to look like this:
$VAR1 = {
'shaders' => {
'stock_gui.vert' => '',
'stock_font.vert' => '',
'stock_gui.frag' => '',
'stock_font.frag' => ''
},
'textures' => {},
'fonts' => {
'DroidSansMono.ttf' => '',
'small' => {
'DroidSansMono.ttf' => ''
}
}
};
Now I am trying to dfs iterate for example the fonts sub tree:
push (#stack, \%{$myHash->{'fonts'}});
Then in a loop:
my $tmpHash = pop(#stack);
Then a dumper of $tmpHash shows:
$VAR1 = {
'DroidSansMono.ttf' => '',
'small' => {
'DroidSansMono.ttf' => ''
}
};
The problem is trying access the children of the hash reference. I can count the keys and see the children. The dumper output looks okay. However trying to do something like:
foreach my $childKey ( keys $tmpHash ){
my $subChildrenCount = scalar keys(%{$tmpHash->{$childKey}});
}
Yields the error:
Can't use string ("") as a HASH ref while "strict refs" in use
I think this is because $tmpHash is a hash reference. I likely just need to dereference it somewhere. I've tried many things and all yields further issues. Any help appreciated.
If I try:
%{$tmpHash->{'small'}}
Then it works fine.
UPDATE:
If the string contains a '.' then this error occurs. Hard coding 'small' works. Hard coding 'stock_gui.vert' fails unless I escape the '.'. However the keys do not match if I escape the dot...
As you can see by running it yourself,
use strict;
use warnings;
my $tmpHash = {
'DroidSansMono.ttf' => '',
'small' => {
'DroidSansMono.ttf' => ''
}
};
my $subChildrenCount = scalar keys(%{$tmpHash->{'small'}});
the code you say gives that error does not actually give that error. I suspect you are actually doing
my $subChildrenCount = scalar keys(%{$tmpHash->{'DroidSansMono.ttf'}});
Your hash format doesn't make much sense. It mixes field names and actual data as keys.

Perl Hash by reference

so I'm trying to write a subroutine that takes a hash parameter and adds a couple key-value pairs to it (by reference). So far, I've got this:
addParams(\%params);
sub addParams
{
my(%params) = %{$_[0]}; #First argument (as a hash)
$params{"test"} = "testing";
}
But for some reason, It doesn't seem to add the 'test' key. I am new to Perl, but isn't this how you pass a hash by reference? Thanks beforehand.
You can use the hash-ref without de-referencing it:
addParams(\%params);
sub addParams
{
my $params = shift;
$params->{"test"} = "testing";
}
EDIT:
To address your code's issue, when you do:
my(%params) = %{$_[0]};
You're actually making a copy of what the ref points to with %{...}. You can see this via a broken down example (no function, same functionality):
my %hash = ( "foo" => "foo" );
my %copy = %{ \%hash };
$hash{"bar"} = "bar";
$copy{"baz"} = "baz";
print Dumper( \%hash );
print Dumper( \%copy );
Run:
$ ./test.pl
$VAR1 = {
'bar' => 'bar',
'foo' => 'foo'
};
$VAR1 = {
'baz' => 'baz',
'foo' => 'foo'
};
Both hashes have the original 'foo => foo', but now each have their different bar/baz's.

Use of uninitialized value in concatenation (.) or string in slapd_ munin plugin

I'm trying to implement the slapd_ munin plugin which is written in perl which I'm pretty much clueless about. The full plugin is available here. The error I'm getting is this one:
Use of uninitialized value in concatenation (.) or string at
/etc/munin/plugins/slapd_localhost line 232, <DATA> line 275.
Line 232 is this one:
my $searchdn = $ops{$action}->{'search'} . "," . $basedn;
I tried debugging by outputing all the variables/objects as follows:
use Data::Dumper; # top of script
# [...]
print Dumper(%ops);
print "action = [$action]\n";
print "basedn = [$basedn]\n\n";
my $searchdn = $ops{$action}->{'search'} . "," . $basedn;
When I run it again here is what I obtain:
[...] # 15 other variables belonging to $ops
$VAR16 = {
'info' => 'The graph shows the number of Waiters',
'search' => 'cn=Waiters',
'desc' => 'The current number of Waiters',
'filter' => '(|(cn=Write)(cn=Read))',
'title' => 'Number of Waiters',
'label2' => {
'read' => 'Read',
'write' => 'Write'
},
'vlabel' => 'Waiters'
};
action = [localhost]
action = [cn=Monitor]
Use of uninitialized value in concatenation (.) or string at /etc/munin/plugins/slapd_localhost line 237, <DATA> line 275.
Since all the variables seem to be set, I really don't understand the error message I'm getting
Q: Can anybody advise on how debugging this script?
You should dump a reference to %ops, as in
print Dumper \%ops;
This will make the debug output clearer. To illustrate, consider the output of
#! /usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %h = (foo => { bar => 1 }, baz => { quux => 3 });
print "No reference:\n",
Dumper(%h),
"\n",
"Reference:\n",
Dumper(\%h);
Notice how you see the structure much more clearly in the latter half:
No reference:
$VAR1 = 'baz';
$VAR2 = {
'quux' => 3
};
$VAR3 = 'foo';
$VAR4 = {
'bar' => 1
};
Reference:
$VAR1 = {
'baz' => {
'quux' => 3
},
'foo' => {
'bar' => 1
}
};
You cut out a critical bit of the output. What's the value of $VAR15? Is it "localhost" or something else?
When you print $searchdn, what is its value?