Understanding Data::Dumper output - perl

Simple question:
use warnings;
use XML::Simple;
use Data::Dumper;
my $file='test.osm';
my $test_data = XMLin( $file );
print Dumper( $test_data );
my #way_node_refs=$test_data->{'way'}->{'nd'};
print Dumper( #way_node_refs );
print( #way_node_refs[1]->{'ref'} );ere
It has the following output. The first dump is not given because it does not matter.
$VAR1 = [
{
'ref' => '453966480'
},
{
'ref' => '453966490'
},
{
'ref' => '453966482'
},
{
'ref' => '453966130'
},
{
'ref' => '453966143'
},
{
'ref' => '453966480'
}
];
How can I access the values with the key ref. I don't know how to interprete the braces kinda.

This is called array of hashes. If $array contains the data then below will work :
[] is anonymous array reference, while {} is anonymous hash reference. Check perlref.
for (#{ $array }) {
print $_ -> {ref},"\n";
}
Further reading:
Using Arrow operator(->) - Most suitable for accesing a single item from arrayref or hashref.
Using # { } - Suitable for iterating over arrayrefs .
Edit:
Now after your edit, it is clear that you want something like this:
for (#way_node_refs) {
print $_ -> {ref},"\n";
}

You have an array reference where the values inside the array are hash references. To access single values, use the arrow operator ->.
print $foo->[0]->{ref};
print $foo->[1]->{ref};
Or you can loop through them.
foreach my $elem ( #{ $foo } ) {
print Dumper $elem;
print $elem->{ref};
}
You could even sort them alphabetically by the values of the ref keys.
my #sorted = sort { $a->{ref} cmp $b->{ref} } #$foo; # note #sorted is an array
See perlref, perlreftut and perldsc for more details on data structures and references.

Related

I can't print keys and values in Perl

I have a data structure that I got from this code.
my $name = $data->{Instances}->[0]->{Tags};
That data structure looks like this
$VAR1 = [
{
'Key' => 'Name',
'Value' => 'fl-demo'
},
{
'Value' => 'FL',
'Key' => 'state'
}
];
I'm trying to print the keys and values with this
foreach my $key (sort keys %$name) {
my $value = $name->{$key};
print "$key => $value\n";
}
I'm getting
Not a HASH reference at ./x.pl line 19.
The tags are returned as an array, not a hash. So you're looking at doing something like this, instead, to iterate over them:
foreach my $tag (#$name) {
my $key = $tag->{Key};
my $val = $tag->{Value};
print "$key => $val\n";
}
The data structure dump of variable $name indicates that you have array reference.
You can use loop to output the data of interest, do not forget to dereference $name variable.
use strict;
use warnings;
use feature 'say';
my $name = [
{
'Key' => 'Name',
'Value' => 'fl-demo'
},
{
'Value' => 'FL',
'Key' => 'state'
}
];
say "$_->{Key} = $_->{Value}" for #$name;
Output
Name = fl-demo
state = FL
Elaborating on a previous answer:
$name is a reference to an array containing references to hashes.
#$name and #{$name} (equivalent representations) refer to the array that $name references.
${$name}[0] and $name->[0] (equivalent representations) refer to the first hash in the array referenced by $name.
${$name}[0]{'Key'}, $name->[0]->{'Key'}, etc. (equivalent representations) refer to 'Key''s hash value in the first hash in the array referenced by $name.
As such, the following would iterate over all array and hash elements:
foreach my $hashref ( #{$name} )
{
foreach my $key ( sort(keys(%{$hashref})) )
{
printf("%s => %s\n",$key,$hashref->{$key});
}
print "\n";
}
Or, more compactly (and arguably unreadably):
printf("%s\n",join("\n", map {
my $h = $_;
join(', ', map { sprintf('%s=%s',$_,$h->{$_}) } sort(keys(%{$h})) );
} #{$name} ));

Hash content extraction based on condition

I have a hash containing node data.
I am expecting hash content to be printed in -r_<count> and -d_<count> attributes.
Here is the script:
use strict; use warnings;
use Data::Dumper;
my %hash = (
'Network=Test,Cell=31' => [ 'Network=Test,Unit=RU-1-1,Port=A',
'Network=Test,Unit=RU-1-2,Port=A'
],
'Network=Test,Cell=32' => [ 'Network=Test,Unit=RU-1-1,Port=A',
'Network=Test,Unit=RU-1-2,Port=A'
],
'Network=Test,Cell=33' => [ 'Network=Test,Unit=RU-1-5,Port=A',
'Network=Test,Unit=RU-1-6,Port=A'
],
);
print "hash:\n".Dumper(\%hash);
my $count = 0;
foreach my $d (sort keys %hash) {
$count++;
print "-d_". $count."=".$d . "\n";
my %seen = ();
foreach my $r (sort #{$hash{$d}}) {
$seen{$r}++;
}
if ((keys %seen) > 0) {
my $uniq = join ("###",sort keys %seen);
print "-r_". $count . "=" . $uniq . "\n";
} else {
print "-r_". $count."="."NA\n";
}
}
And I am able to print output like below(current output):
-d_1=Network=Test,Cell=31
-r_1=Network=Test,Unit=RU-1-1,Port=A###Network=Test,Unit=RU-1-2,Port=A
-d_2=Network=Test,Cell=32
-r_2=Network=Test,Unit=RU-1-1,Port=A###Network=Test,Unit=RU-1-2,Port=A
-d_3=Network=Test,Cell=33
-r_3=Network=Test,Unit=RU-1-5,Port=A###Network=Test,Unit=RU-1-6,Port=A
But I want output to be printed like below (expected output):
-r_1=Network=Test,Unit=RU-1-1,Port=A
-d_1=Network=Test,Cell=31###Network=Test,Cell=32
-r_2=Network=Test,Unit=RU-1-2,Port=A
-d_2=Network=Test,Cell=31###Network=Test,Cell=32
-r_3=Network=Test,Unit=RU-1-5,Port=A
-d_3=Network=Test,Cell=33
-r_4=Network=Test,Unit=RU-1-6,Port=A
-d_4=Network=Test,Cell=33
The expected output is, the value of -r_<count> should be printed as singular (from %hash keys array value) and -d_<count> (from %hash keys) should printed.
The output is guided by the unique values of the arrays. Your output loop must therefore iterate over these.
(
'Network=Test,Unit=RU-1-1,Port=A',
'Network=Test,Unit=RU-1-2,Port=A',
'Network=Test,Unit=RU-1-5,Port=A',
'Network=Test,Unit=RU-1-6,Port=A',
)
However, for each of these, the output needs the associated keys. This means the output loop requires the following data:
(
'Network=Test,Unit=RU-1-1,Port=A' => [ 'Network=Test,Cell=31', 'Network=Test,Cell=32' ],
'Network=Test,Unit=RU-1-2,Port=A' => [ 'Network=Test,Cell=31', 'Network=Test,Cell=32' ],
'Network=Test,Unit=RU-1-5,Port=A' => [ 'Network=Test,Cell=33' ],
'Network=Test,Unit=RU-1-6,Port=A' => [ 'Network=Test,Cell=33' ],
)
Basically, your data structure is inside-out. But now that we know what we want, it's just a question of transforming the data structure into what we need.
my %foos_by_bar;
for my $foo (keys %hash) { # %hash_b
my $bars = $hash{$foo}; # %hash_a
for my $bar (#$bars) {
push #{ $foos_by_bar{$bar} }, $foo;
}
}
The output loop simply needs to iterate over the (possibly sorted) keys of %foos_by_bar, and #{ $foos_by_bar{$bar} } contains the data you need for -d.
Nothing's stopping you from iterating over the sorted keys of %foos_by_bar in the output loop to produce predictable output, but that won't necessarily give you the same order as in the question. If you need that specific order, you can use the following:
my #bars;
my %foos_by_bar;
for my $foo (sort keys %hash) { # %hash_b
my $bars = $hash{$foo}; # %hash_a
for my $bar (#$bars) {
push #bars, $bar if !$foos_by_bar{$bar};
push #{ $foos_by_bar{$bar} }, $foo;
}
}
In this case, the output loop would iterate over #bars.

Accessing and modifying a nested hash based on a dot separated string

I have a string as input, say apple.mango.orange = 100
I also have a hash reference:
$inst = {
'banana' => 2,
'guava' => 3,
'apple' => {
'mango' => {
'orange' => 80
}
}
};
I want to modify the value of orange using the input string. Can someone please help me how I could do this?
I tried splitting the string into (key, value) pair. I then did the following on the key string:
my $key2 = "\$inst->{".$key."}";
$key2 =~ s/\./}->{/g;
$$key2 = $value;
This does not work as intended. Can someone help me out here? I have read the Perl FAQ about not using a variable value as variable but I am unable to think of an alternative.
You are building string that consists of (buggy) Perl code, but you never ask Perl to execute it. ...but that's not the right approach.
sub dive_val :lvalue {
my $p = \shift;
$p = \($$p->{$_}) for #_;
$$p
}
my #key = split /\./, "apple.mango.orange";
dive_val($inst, #key) = $value;
or
use Data::Diver qw( DiveVal );
my #key = split /\./, "apple.mango.orange";
DiveVal($inst, map \$_, #key) = $value;
Not only is a symbolic reference a very bad idea here, it doesn't even solve your problem. You're building an expression in $key2, and just jamming another dollar sign in front of its name won't make perl execute that code. For that you would need eval, which is another bad idea
You can install and use the Data::Diver module, which does exactly this sort of thing, or you can simply loop over the list of hash keys, picking up a new hash reference each time and assigning the value to the element with the last key
The biggest issue is actually parsing the incoming string into a list of keys and a value. This code implements a subroutine apply which applies the implied operation in the string to a nested hash. Unless you are confident of your data, it needs some error checking addingto make sure each of the keys in the list exists. The Data:;Dumper output is just to demonstrate the validity of the result
use strict;
use warnings 'all';
use Data::Dumper;
my $inst = { 'banana' => 2, 'guava' => 3, 'apple' => { 'mango' => { 'orange' => 80 } } };
my $s = 'apple.mango.orange = 100';
apply($s, $inst);
print Dumper $inst;
sub apply {
my ($operation, $data) = #_;
my ($keys, $val) = $operation =~ /([\w.]+)\s*=\s*(\d+)/;
my #keys = split /\./, $keys;
my $last = pop #keys;
my $hash = $data;
$hash = $hash->{$_} for #keys;
$hash->{$last} = $val;
}
output
$VAR1 = {
'banana' => 2,
'apple' => {
'mango' => {
'orange' => '100'
}
},
'guava' => 3
};

Hash doesn't print in Perl

I have a hash:
while( my( $key, $value ) = each %sorted_features ){
print "$key: $value\n";
}
but I cannot obtain the correct value for $value. It gives me:
intron: ARRAY(0x3430440)
source: ARRAY(0x34303b0)
exon: ARRAY(0x34303f8)
sig_peptide: ARRAY(0x33f0a48)
mat_peptide: ARRAY(0x3430008)
Why is it?
Your values are array references. You need to do something like
while( my( $key, $value ) = each %sorted_features ) {
print "$key: #$value\n";
}
In other words, dereference the reference. If you are unsure what your data looks like, a good idea is to use the Data::Dumper module:
use Data::Dumper;
print Dumper \%sorted_features;
You will see something like:
$VAR1 = {
'intron' => [
1,
2,
3
]
};
Where { denotes the start of a hash reference, and [ an array reference.
You can use also Data::Dumper::Pertidy which runs the output of Data::Dump through Perltidy.
#!/usr/bin/perl -w
use strict;
use Data::Dumper::Perltidy;
my $data = [{title=>'This is a test header'},{data_range=>
[0,0,3, 9]},{format => 'bold' }];
print Dumper $data;
Prints:
$VAR1 = [
{ 'title' => 'This is a test header' },
{ 'data_range' => [ 0, 0, 3, 9 ] },
{ 'format' => 'bold' }
];
Your hash values are array references. You need to write additional code to display the contents of these arrays, but if you are just debugging then it is probably simpler to use Data::Dumper like this
use Data::Dumper;
$Data::Dumper::Useqq = 1;
print Dumper \%sorted_features;
And, by the way, the name %sorted_features of your hash worries me. hashes are inherently unsorted, and the order that each retrieves the elements is essentially random.

how to declare array reference in hash refrence

my $memType = [];
my $portOp = [];
my $fo = "aster.out.DRAMA.READ.gz";
if($fo =~/aster.out\.(.*)\.(.*)\.gz/){
push (#{$memType},$1);
push (#{$portOp},$2);
}
print Dumper #{$memType};
foreach my $mem (keys %{$portCapability->{#{$memType}}}){
//How to use the array ref memType inside a hash//
print "entered here\n";
//cannot post the rest of the code for obvious reasons//
}
I am not able to enter the foreach loop . Can anyone help me fix it?
Sorry this is not the complete code . Please help me.
%{$portCapability->{#{$memType}}}
This doesn't do what you may think it means.
You treat $portCapability->{#{$memType}} as a hash reference.
The #{$memType} is evaluated in scalar context, thus giving the size of the array.
I aren't quite sure what you want, but would
%{ $portCapability->{ $memType->[0] } }
work?
If, however, you want to slice the elements in $portCapability, you would need somethink like
#{ $portCapability }{ #$memType }
This evaluates to a list of hashrefs. You can then loop over the hashrefs, and loop over the keys in an inner loop:
for my $hash (#{ $portCapability }{ #$memType }) {
for my $key (keys %$hash) {
...;
}
}
If you want a flat list of all keys of the inner hashes, but don't need the hashes themselves, you could shorten above code to
for my $key (map {keys %$_} #{ $portCapability }{ #$memType }) {
...;
}
I think what you want is this:
my $foo = {
asdf => {
a => 1, b => 2,
},
foo => {
c => 3, d => 4
},
bar => {
e => 5, f => 6
}
};
my #keys = qw( asdf foo );
foreach my $k ( map { keys %{ $foo->{$_} } } #keys ) {
say $k;
}
But you do not know which of these $k belongs to which key of $foo now.
There's no direct way to get the keys of multiple things at the same time. It doesn't matter if these things are hashrefs that are stored within the same hashref under different keys, or if they are seperate variables. What you have to do is build that list yourself, by looking at each of the things in turn. That's simply done with above map statement.
First, look at all the keys in $foo. Then for each of these, return the keys inside that element.
my $memType = [];
my $portOp = [];
my $fo = “aster.out.DRAMA.READ.gz”;
if ($fo =~ /aster.out\.(\w+)\.(\w+)\.gz/ ) { #This regular expression is safer
push (#$memType, $1);
push (#$portOp, $2);
}
print Dumper “#$memType”; #should print “DRAMA”
#Now if you have earlier in your program the hash %portCapability, your code can be:
foreach $mem (#$memType) {
print $portCapability{$mem};
}
#or if you have the hash $portCapability = {…}, your code can be:
foreach $mem (#$memType) {
print $portCapability->{$mem};
}
#Hope it helps