How to address specific array element in complex data structure in perl - perl

I have a complex json data structure in perl like in the following example. I want to address an array element and store data.
Variable
$VAR1 = {
'dummy' => 'foo',
'profiles' => {
'Tags' => [
{
'###PLACEHOLDER###',
}
],
}
I can for example add an element at "###PLACEHOLDER###" but want later in the perl script to add beneath that Placeholder additional information.
Normally i would address these elements with $var->{profiles}->{Tags}->{PLACEHOLDER} but this is not working with an array.
I dont want to create everytime a foreach loop when i know the name exactly.
Any advice?

[UPDATE: used dpathr instead of dpath for the references to structures]
[UPDATE: used dpath instead of dpathr for the references to elements]
Data::DPath can do what you require. Here's code which returns a reference to any structure (hash or array) which contains an element whose value is ###PLACEHOLDER###:
use strict;
use warnings;
use Data::Dumper;
use Data::DPath qw[ dpath dpathr ];
my $struct = {
'dummy' => 'foo',
'profiles' => {
'ARRAY' => [ '###PLACEHOLDER###' ],
'HASH' => { key => '###PLACEHOLDER###' },
},
};
my $path = dpath( '//[value eq "###PLACEHOLDER###"]/..' );
my #matches = $path->match( $struct );
print Dumper \#matches;
It results in:
$VAR1 = [
[
'###PLACEHOLDER###'
],
{
'key' => '###PLACEHOLDER###'
}
];
If you want direct access to the element, change the path to
my $path = dpathr( '//*[value eq "###PLACEHOLDER###"]' );
with the result:
$VAR1 = [
\'###PLACEHOLDER###',
\'###PLACEHOLDER###'
];

It's not clear to me what you what "adding an element at ###PLACEHOLDER###" means. Elements can be added to arrays and hashes, and it's not clear to which array or hash you are referring.
To append an element to the array referenced by $var->{profiles}{Tags}, use
push #{ $var->{profiles}{Tags} }, $val;
This results in
$VAR1 = {
'dummy' => 'foo',
'profiles' => {
'Tags' => [
{
'###PLACEHOLDER###' => undef,
},
$val
],
}
To add an element to the hash referenced by the last element of the array referenced by $var->{profiles}{Tags}, use
$var->{profiles}{Tags}[-1]{$key} = $val;
This results in
$VAR1 = {
'dummy' => 'foo',
'profiles' => {
'Tags' => [
{
'###PLACEHOLDER###' => undef,
$key => $val,
},
],
}
Of course, if $key is ###PLACEHOLDER###, this results in
$VAR1 = {
'dummy' => 'foo',
'profiles' => {
'Tags' => [
{
'###PLACEHOLDER###' => $val,
},
],
}

Related

Print data after dumper

I have this structure with data-dumper:
$VAR1 = {
'field' => [
{
'content' => {
'en' => [
'Footware haberdashery leather goods'
],
'de' => [
'Schuhe Kurzwaren und Lederartikel'
],
'it' => [
'Calzature mercerie e pelletterie'
]
},
'type' => 'tag',
'valore' => 'TAG3'
},
{
'content' => {
'en' => [
'Cobbler'
],
'de' => [
'Schuster'
],
'it' => [
'Calzolai'
]
},
'type' => 'tag',
'valore' => 'TAG24'
}
]
};
My question is: how to take data and print one for one ?
I want print the name, the tag and valore.
For my software is necessary take the name of shop and more data for example the type
It looks like the structure is a hashref containing an arrayref of hashes, and so on. And apparently where you mention 'name' you mean 'content' by language. Likewise, it seems that where you mention 'tag' you mean 'type'. My answer will be based on those assumptions.
foreach my $rec (#{$href->{field}}) {
print "$rec->{content}->{en}->[0]: $rec->{type}, $rec->{valore}\n";
}
The -> between {content} and {en}, and again between {en} and [0] are optional, and a matter of style.
If you simply want to access elements directly (foregoing the loop), you might do it like this:
print $href->{field}->[0]->{content}->{en}->[0], "\n";
print $href->{field}->[0]->{type}, "\n";
print $href->{field}->[0]->{valore}, "\n";
If you want to print all the languages, you could do this:
foreach my $rec (#{$href->{field}}) {
print $rec->{content}->{$_}->[0], "\n" foreach sort keys %{$rec->{content}};
print $rec->{type}, "\n";
print $rec->{valor}, "\n\n";
}
There are several Perl documentation pages that could be of use to you in the future as you learn to manipulate references and datastructures with Perl: perlreftut, perlref, and perldsc. Access them from your own system as perldoc perlreftut, for example.

Perl- Get Hash Value from Multi level hash

I have a 3 dimension hash that I need to extract the data in it. I need to extract the name and vendor under vuln_soft-> prod. So far, I manage to extract the "cve_id" by using the following code:
foreach my $resultHash_entry (keys %hash){
my $cve_id = $hash{$resultHash_entry}{'cve_id'};
}
Can someone please provide a solution on how to extract the name and vendor. Thanks in advance.
%hash = {
'CVE-2015-6929' => {
'cve_id' => 'CVE-2015-6929',
'vuln_soft' => {
'prod' => {
'vendor' => 'win',
'name' => 'win 8.1',
'vers' => {
'vers' => '',
'num' => ''
}
},
'prod' => {
'vendor' => 'win',
'name' => 'win xp',
'vers' => {
'vers' => '',
'num' => ''
}
}
},
'CVE-2015-0616' => {
'cve_id' => 'CVE-2015-0616',
'vuln_soft' => {
'prod' => {
'name' => 'unity_connection',
'vendor' => 'cisco'
}
}
}
}
First, to initialize a hash, you use my %hash = (...); (note the parens, not curly braces). Using {} declares a hash reference, which you have done. You should always use strict; and use warnings;.
To answer the question:
for my $resultHash_entry (keys %hash){
print "$hash{$resultHash_entry}->{vuln_soft}{prod}{name}\n";
print "$hash{$resultHash_entry}->{vuln_soft}{prod}{vendor}\n";
}
...which could be slightly simplified to:
for my $resultHash_entry (keys %hash){
print "$hash{$resultHash_entry}{vuln_soft}{prod}{name}\n";
print "$hash{$resultHash_entry}{vuln_soft}{prod}{vendor}\n";
}
because Perl always knows for certain that any deeper entries than the first one is always a reference, so the deref operator -> isn't needed here.

Odd number of elements in Perl hash

I'm having some trouble with hashes. I need to get a list of version with key => value pairs into a hash but kept getting an error. The code just below is my recent attempt. Some of the strings have been changed and unneeded code isn't included.
I've looked all over the net but haven't been able to find anything that can help me. I've used Perl for a long time but haven't used hashes and arrays much. Most of my Perl experience has had to do with regex and shell execution. If I was using PHP, I would just use a multidimensional array but this is Perl and there's a lot more to the script I'm writing than what is shown or I'd switch to PHP.
I appreciate whoever takes the time to help. Thanks!
sub sub1 {
# Read file which populates #rows with each line.
my %data;
for (my $i=2; $i <= scalar #rows - 1; $i++) {
$ver =~ s/\s//m;
$data{ $ver } = [
'version', $ver,
'available', $table_tree->cell($jt,1)->as_text,
'bsupport', $table_tree->cell($jt,2)->as_text,
'ssupport', $table_tree->cell($jt,3)->as_text,
'eol', $table_tree->cell($jt,3)->as_text,
'utype', $table_tree->cell($jt,5)->as_text,
'lreleases', $table_tree->cell($jt,7)->as_text
];
};
return %data;
}
sub check_ {
# line 199 follows
my (%hash) = #_;
print Dumper (\%hash)."\n";
}
my %data = sub1($file);
check_(%data);
Warning:
Odd number of elements in hash assignment at ./file.pl line 199 (#1)
(W misc) You specified an odd number of elements to initialize a hash,
which is odd, because hashes come in key/value pairs.
The %hash when dumped is:
$VAR1 = {
'string1' => [
'version',
'string1',
'available',
'stringa',
'bsupport',
'stringb',
'ssupport',
'stringc',
'eol',
'stringd',
'utype',
'stringe',
'lreleases',
'stringf'
],
'string2' => [
'version',
'string2',
'available',
'stringa',
'bsupport',
'stringb',
'ssupport',
'stringc',
'eol',
'stringd',
'utype',
'stringe',
'lreleases',
'stringf'
],
'string3' => [
'version',
'string3',
'available',
'stringa',
'bsupport',
'stringb',
'ssupport',
'stringc',
'eol',
'stringd',
'utype',
'stringe',
'lreleases',
'stringf'
],
# ...
}
I was originally trying to have my has be as follows. Where $VAR1 = { 'stringN' => { ... } would be any number with any number of key => value pairs but would also get the same error. I had it working but it would always generate the error.
$VAR1 = {
'string1' => {
'version' => 'string1',
'available' => 'stringa',
'bsupport' => 'stringb',
'ssupport' => 'stringc',
'eol' => 'stringd',
'utype' => 'stringe',
'lreleases' => 'stringf'
},
'string2' => {
'version' => 'string2',
'available' => 'stringa',
'bsupport' => 'stringb',
'ssupport' => 'stringc',
'eol' => 'stringd',
'utype' => 'stringe',
'lreleases' => 'stringf'
},
'string3' => {
'version' => 'string3',
'available' => 'stringa',
'bsupport' => 'stringb',
'ssupport' => 'stringc',
'eol' => 'stringd',
'utype' => 'stringe',
'lreleases' => 'stringf'
}
# ...
}
It's because you're using [ which is the anonymous array constructor. Try using { instead.
And it might be a bit more idiomatic if you did:
$data{ $ver } = {
version => $jver,
available => $table_tree->cell($jt,1)->as_text,
};
Oh, and indent your code. That for loop doesn't finish where you (might!) think it does. Especially - check where the return happens. (And what $jt is set to - it appears unrelated to $i)

Hash not passing properly between functions

I set up a user hash in a function with the following,
push #{$profile{$index}{$infoName}}, $information
and print it using print Dumper(\%profile); index++; in the function it was set up it prints each of indexes
`$VAR1 = { '374' => { 'degree' => [ 'CS' ], 'birthdate' => [ '1973/12/13' ], 'gender' => [ 'M' ],...}
$VAR1 = { '375' => { 'degree' => [ 'CS' ], 'birthdate' => [ '1933/02/03' ], 'gender' => [ 'F' ],...}`
when i try to access this within another function's foreach loop using print "${$profile{$currIndex}{'gender'}}"; i get odd behaviour where the print returns an empty string and get some random numbers appear in the hash: '$VAR1 = { '4' => {}, '1' => {}, '3' => {}, '2' => {}, '378' => { 'birthdate' => [ '1961/03/29' ], 'gender' => ['F'],..}
How can i properly access the gender feild from within a loop?
push #{$profile{$index}{$infoName}}, $information;
print "${$profile{$currIndex}{'gender'}}";
I'm not even sure what the second line actually does. On my Ubuntu, perl produces an error: not a scalar reference.
What you want is, to print all array elements:
print "#{$profile{$currIndex}{'gender'}}\n";
or, to print the first:
print $profile{$currIndex}->{'gender'}->[0], "\n";
The leaf element is an array references and has to be dereferenced as such.
I'm not sure why you use there array references. In your sample data there are no multiple elements in the arrays. Probably you wanted to write simply this? -
$profile{$index}{$infoName} = $information;
...
print "$profile{$currIndex}{'gender'}\n";

Possible to detect hash with more than one key?

I am collecting data in a hash of hashes which looks like
$VAR1 = {
'502' => {
'user2' => '0'
},
'501' => {
'git' => '0',
'fffff' => '755'
},
'19197' => {
'user4' => '755'
}
};
The problem is in 501. Two keys may not occur. Is it possible to detect this?
Update
Fixed typo in hash.
If you are only going to store one key-value pair under each key of the main hash, why not use a 2-element array instead? That way you can check for existence before making each new insert, without needing to check the size of the hash or knowing what its keys are. The structure I'm proposing is this:
$VAR1 = {
'502' => [ 'user2', '0' ],
'501' => [ 'git', '0' ],
'19197' => [ 'user4', '755' ]
}
Assuming your hashref above is named $var :
my #bad = grep { scalar keys %{$var->{$_}} > 1 } keys %$var;
Results in an array of hash keys that have more than one hashref within them. Using your data above:
# perl test.pl
$VAR1 = {
'501' => {
'git' => '0',
'fffff' => '755'
},
'502' => {
'user2' => '0'
},
'19197' => {
'user4' => '755'
}
};
$VAR1 = '501';
Then you could access each element that is detected as bad with:
foreach my $key ( #bad ) {
# do something to or with $var->{$key}
}
keys(%{$VAR1{'501'}}) == 2 where the rest would be one.
Also, syntax error on that key, but I assume it's a typo.