Perl printing of a hash gives ARRAY(xxxxxxx) - perl

I know there are many questions already with this kind of subject, but as far as I know (perl beginner so I could be wrong), I'm not using an array so I don't understand where this output comes from
$VAR1 = {
'BridgeMode' => {
'Ten-GigabitEthernet1/0/5' => {
'Description' => 'poort1',
'Duplex' => 'F(a)',
'Interface' => 'Ten-GigabitEthernet1/0/5',
'Link' => 'UP',
'PVID' => '100',
'Speed' => '10G(a)',
'Type' => 'A',
'Vlan100' => {
'UntaggedPorts' => [
'Ten-GigabitEthernet1/0/5'
]
},
'vlanID' => [
'Vlan100'
]
},
Above is the content of my dumper and this is my print statement:
my $untaggedInterface = $data{BridgeMode}{"Ten-GigabitEthernet1/0/5"}{Vlan100}{UntaggedPorts} ;
print "Untagged: $untaggedInterface \n" ;
I would expect that the print statement would print "Ten-GigabitEthernet1/0/5" but instead it shows this:
Untagged: ARRAY(0x24a8ec0)
edit - it is possible that there exists an tagged and an untagged:
$VAR1 = {
'BridgeMode' => {
'Ten-GigabitEthernet1/0/12' => {
'Description' => 'poort5',
'Duplex' => 'F(a)',
'Interface' => 'Ten-GigabitEthernet1/0/12',
'Link' => 'UP',
'PVID' => '100',
'Speed' => '10G(a)',
'Type' => 'H',
'Vlan100' => {
'UntaggedPorts' => [
'Ten-GigabitEthernet1/0/12'
]
},
'Vlan107' => {
'TaggedPorts' => [
'Ten-GigabitEthernet1/0/12'
]
},
edit: printing the content of the array
my #untaggedInterface = $data{BridgeMode}{"Ten-GigabitEthernet1/0/5"}{Vlan100}{UntaggedPorts} ;
print join(", ", #untaggedInterface) ;
stil gives
ARRAY(0x1c03a68)

You would get the expected result if you had the following, i.e. a string instead of a string array:
'UntaggedPorts' => 'Ten-GigabitEthernet1/0/12'
Otherwise, you must specify the index of the array element:
my $untaggedInterface = $data{BridgeMode}{"Ten-GigabitEthernet1/0/5"}{Vlan100}{UntaggedPorts}[0];

Related

Parsing data elements and it's attributes from dataset into an array

I have a data set with different types of plans, the data below shows the activeplans and pastplans elements, (and other plans not included in the example).
I think the data can be represented as follows
$data->activeplans->activeplanloc[activeplan->{}, activeplan->{},
...activeplan->{}] $data->pastplans->pastplanloc[pastplan->{},
pastplan->{}, ...pastplan->{}]
Each plan element has number of attributes, for instance id, lat, long, numpersons (and other attributes not included in the example)
My goal is to loop through all the plan items and extract the attributes.
Also note, the ...planloc[] outer element and the lat/long fields it contains along with the empty ...plan[] - can be ignored.
This is the loop I tried to do it with, but I'm stuck on exacting the activeplan elements, can you help correct my syntax error, I don't now how to properly load the elements into an array given this data stucture?
foreach my $planArrayItem (#{$data->{"activeplans"}->{"activeplanloc"}->{"activeplan"}{}}) {
#...
if (exists $planArrayItem->{numpersons}) {
$tmp .= "<li>Number of personal: $projArrayItem->{numpersons}</li>";
}
#...
}
Oh, and this is the data set.
{ 'updatetime' => '3/24/2021 11:44:19 AM', 'pastplans' =>
{ 'pastplanloc' => [ { 'longitude' => '-29.51502', 'latitude' =>
'32.307558', 'pastplan' => { 'planclass' => 'A', 'longitude' =>
'-29.51502', 'id' => '211', 'latitude' => '32.307558',
'numlocations' => '15' } }, { 'longitude' => '-28.798305',
'latitude' => '32.656135', 'pastplan' => [ { 'id' => '214',
'longitude' => '-28.798305', 'latitude' => '32.656135',
'planclass' => 'E', 'numlocations' => '16' }, { 'longitude' =>
'-28.798305', 'id' => '215', 'latitude' => '32.656135', 'planclass'
=> 'C', 'numlocations' => '21' } ] } ] }, 'activeplans' =>
{ 'activeplanloc' => [ { 'latitude' => '33.132491', 'activeplan'
=> [ { 'planclass' => 'B', 'longitude' => '-25.304968', 'id' =>
'942', 'latitude' => '33.132491', 'numpersons' => '17' },
{ 'numpersons' => '21', 'planclass' => 'G', 'id' => '943',
'longitude' => '-25.304968', 'latitude' => '33.132491' } ],
'longitude' => '-25.304968' }, { 'latitude' => '33.097290',
'activeplan' => { 'numpersons' => '31', 'id' => '944',
'longitude' => '-25.295086', 'latitude' => '33.097290',
'planclass' => 'M' }, 'longitude' => '-25.295086' } ] } };
This is the XML format if there is a better way to format it while reading in perhaps?
<?xml version="1.0" encoding="utf-8"?>
<plans>
<updatetime>3/24/2021 11:44:19 AM</updatetime>
<pastplans>
<pastplanloc latitude="32.307558" longitude="-29.51502">
<pastplan planclass="A" id="211" numpersons="15" latitude="32.307558" longitude="-29.51502"/>
</pastplanloc>
<pastplanloc latitude="32.656135" longitude="-28.798305">
<pastplan planclass="E" id="214" numpersons="16" latitude="32.656135" longitude="-28.798305"/>
<pastplan planclass="C" id="215" numpersons="21" latitude="32.656135" longitude="-28.798305"/>
</pastplanloc>
</pastplans>
<activeplans>
<activeplanloc latitude="33.132491" longitude="-25.304968">
<activeplan planclass="B" id="942" numpersons="17" latitude="33.132491" longitude="-25.304968"/>
<activeplan planclass="G" id="943" numpersons="21" latitude="33.132491" longitude="-25.304968"/>
</activeplanloc>
<activeplanloc latitude="33.097290" longitude="-25.295086">
<activeplan planclass="M" id="944" numpersons="31" latitude="33.097290" longitude="-25.295086"/>
</activeplanloc>
</activeplans>
</plans>
I am quite certain that:
foreach my $planArrayItem (#{$data->{"activeplans"}->{"activeplanloc"}}) {
#...
if ($planArrayItem->{"activeplan"}{numpersons}) {
$tmp.= "<li>Number of personal: ".$planArrayItem->{"activeplan"}->{numpersons}."</li>";
}
}
is the code you are looking for. As you stated above "activeplanloc" contains an array which reference an activeplan. So the outer loop has to iterate over this.
With your new "data set", the correct code is:
foreach my $planArrayItem (#{$data->{"activeplans"}->{"activeplanloc"}}) {
#...
my $plans = ref $planArrayItem->{"activeplan"} eq "ARRAY" ?
$planArrayItem->{"activeplan"} : [$planArrayItem->{"activeplan"}];
foreach my $plan (#$plans) {
if ($plan->{numpersons}) {
$tmp .= "<li>Number of personal: ".$plan->{numpersons}."</li>";
}
}
}
See https://pastebin.com/QeD6ZwZz for a working example

Iterate an array reference and convert to hash in perl

I have an hash (Printed by Dumper) which is described below
$VAR1 = {
'items' => [
{
'name' => 'test1',
'id' => '1',
'desc' => 'desc1',
},
{
'name' => 'test2',
'id' => '2',
'desc' => 'desc2',
}
],
};
I need to convert "items" which is array reference to a hash like below. ('items' will be a hash of hash with the value of 'id' being the key)
$VAR1 = {
'items' => {
'1' =>{
'name' => 'test1',
'id' => '1',
'desc' => 'desc1',
},
'2' => {
'name' => 'test2',
'id' => '2',
'desc' => 'desc2',
}
}
};
Lets start with the below code. (Assume $data represents the original data and $newitems represents the modified items)
my $data;
my $items = $data->{items};
my %newitems;
foreach my $element (#$items) {
......
}
This looks like an XY problem to me - I'm guessing you're trying to transform some XML, so I'd suggest you want to look upstream to solve this problem.
But on the offchance you're not, then:
$data -> {items} = { map { $_ -> {id} => $_ } #{$data->{items} } };

Accessing a nested data structure

I have an array of hashes nested to multiple levels. I need to extract a value from all deeply-nested hashes that have a given value for a different key in the same hash
This is a collection of entities from our database, and the data represents contacts within each entity and all of their contact values.
There is a hash key contact_method_type_id which refers to an integer defining the type of contact method. The contact_method_type_id that I care about is 1, which is email.
The first contact has three different contact_methods. The first is 4 which is an office phone, the second is a 2 which is a home phone, and the third is a 1 which is email.
Within the same hash is there is a 'contact_method_value', which is the string representation of their email address.
I need a way to extract just these values into a new array
Here are the contents of the first element of the array
$VAR1 = [
{ 'total' => '2',
'results' => [
{ 'contact_type_name' => 'Primary Technical Contact',
'street' => undef,
'state_id' => undef,
'state_name' => undef,
'last_name' => 'Barb',
'entities' => [
{ 'entity_name' => 'XXXXX',
'entity_id' => 'XXXXX'
}
],
'state_abbr_name' => undef,
'city' => undef,
'country_id' => undef,
'latitude' => undef,
'contact_id' => 'XXXXXX',
'contact_type_id' => '1',
'roles' => [],
'contact_methods' => [
{ 'entity_name' => undef,
'contact_method_value' => 'XXXXXXX',
'contact_method_type_id' => '4',
'contact_method_id' => '24041',
'entity_id' => undef,
'contact_method_type_name' => 'Cell Phone'
},
{ 'entity_name' => undef,
'contact_method_value' => 'XXXXXX',
'contact_method_type_id' => '2',
'contact_method_id' => '24051',
'entity_id' => undef,
'contact_method_type_name' => 'Office Phone'
},
{ 'entity_name' => undef,
'contact_method_value' => 'example#example.com',
'contact_method_type_id' => '1',
'contact_method_id' => '24061',
'entity_id' => undef,
'contact_method_type_name' => 'Email'
}
],
'country_name' => undef,
'longitude' => undef,
'country_abbr_name' => undef,
'full_name' => 'NAME',
'networks' => [
{ 'network_name' => 'NET',
'network_id' => 'X'
}
],
'timezone_id' => undef,
'zip' => undef,
'timezone_name' => undef,
'title' => 'MAC/Network Specialist',
'first_name' => 'Terri'
},
{ 'contact_type_name' => 'Primary Technical Contact',
'street' => 'STREET',
'state_id' => undef,
'state_name' => undef,
'last_name' => 'NAME',
'entities' => [
{ 'entity_name' => 'NAME',
'entity_id' => '2679'
}
],
'state_abbr_name' => undef,
'city' => 'CITY',
'country_id' => undef,
'latitude' => undef,
'contact_id' => '7896',
'contact_type_id' => '1',
'roles' => [],
'contact_methods' => [
{ 'entity_name' => undef,
'contact_method_value' => 'example#example.com',
'contact_method_type_id' => '1',
'contact_method_id' => '16796',
'entity_id' => undef,
'contact_method_type_name' => 'Email'
},
{ 'entity_name' => undef,
'contact_method_value' => 'number',
'contact_method_type_id' => '2',
'contact_method_id' => '16797',
'entity_id' => undef,
'contact_method_type_name' => 'Office Phone'
}
],
'country_name' => undef,
'longitude' => undef,
'country_abbr_name' => undef,
'full_name' => 'NAME',
'networks' => [
{ 'network_name' => 'net',
'network_id' => '17'
}
],
'timezone_id' => undef,
'zip' => 'zip',
'timezone_name' => undef,
'title' => 'Infrastructure Manager',
'first_name' => 'name'
}
],
'offset' => '0'
},
...
This looks suspiciously like something that XML::Simple would have generated.
Assuming this is the case, then I would suggest that you've fallen for the classic mistake of assuming XML::Simple actually helps.
Under that assumption, if you instead use XML::Twig:
Taking your $VAR1. Although - ideally you'll just parse the original source with parse or parsefile:
use XML::Twig;
use XML::Simple;
my $twig = XML::Twig->parse( XMLout($VAR1) );
print $_->att('contact_method_value'), "\n" for $twig->findnodes('//contact_methods[#contact_method_type_name="Email"]');
Which given your sample (as $VAR1):
example#example.com
example#example.com
Edit: Because you've commented that it's JSON then I wouldn't necessarily do this (Although - it does actually work, despite that).
If the data structures are all of the same kind, this is very trivial. You just need to iterate all the outer hashrefs (I called those resultsets). Inside those, you need to look at all results, and in each result you need to look at all the contact methods. If one of them has a contact_method_type_id of 1, you take the contact_method_value. And that's it.
my #email_addresses;
foreach my $resultset ( #{$data} ) {
foreach my $result ( #{ $resultset->{results} } ) {
foreach my $contact ( #{ $result->{contact_methods} } ) {
push #email_addresses , [ $contact->{contact_method_value} ]
if $contact->{contact_method_type_id} == 1;
}
}
}
This code assumes your structure is called $data. #email_addresses looks like this when output.
[
[ 'EMAIL' ],
[ 'EMAIL' ]
];
If you have this on a database then you should use an SQL query to retrieve it, rather than fetching everything into memory and processing what you have
The output from Data::Dumper shows the contents of your data, but it doesn't explain what you're dealing with in your code. Specifically, you don't have a $VAR1 in your code, but I have no idea what you do have
In the end, I think I wouldn't start from here. But since it's the only starting point I have to work with, it's a simple matter of recursing through the data structure
I've assumed that you want
$VAR1->[*]{results}[*]{contact_methods}[*]{contact_method_value}
where
$VAR1->[*]{results}[*]{contact_methods}[*]{contact_method_type_name} eq 'Email'
Update
Since your comments I've altered my code to select the same values where
$VAR1->[*]{results}[*]{contact_methods}[*]{contact_method_type_id} == 1
Since you said nothing about your code at all, I've had to assume a variable $data which contains a reference to the array that you show in your question
for my $item ( #$data ) {
my $results = $item->{results};
for my $result ( #$results ) {
my $methods = $result->{contact_methods} or die;
for my $method ( #$methods ) {
#my $type_name = $method->{contact_method_type_name};
#next unless $type_name eq 'Email';
my $type_id = $method->{contact_method_type_id};
next unless $type_id == 1; ## Email
my $value = $method->{contact_method_value};
print "$value\n";
}
}
}
output
example#example.com
example#example.com

Web::Scraper nested structures & elements only containing spesific data

I have the following code to scrape a form for inputs and get the attributes id and name.
#!/usr/bin/perl
use warnings;
use strict;
use URI;
use Data::Dumper::Simple;
use Web::Scraper;
my $urlToScrape = "http://digitalarkivet.arkivverket.no/finn_kilde";
my $scrap = scraper {
process 'div.listGroup.open > ul.grouped > li.expandable', 'data[]' => scraper {
process 'input', 'id' => '#id', name => '#name';
process 'label', 'label_for' => '#for';
process 'span.listExpander ', 'Text' => 'TEXT';
process 'ul.sublist1', 'sublist[]' => scraper {
process 'input', 'id' => '#id', name => '#name';
process 'label', 'label_for' => '#for';
process 'span', 'label' => 'TEXT';
};
};
};
my $res = $scrap->scrape(URI->new($urlToScrape));
print Dumper($res);
which gives me (shortend $res to fit screen better)
$res = {
'data' => [
{
'label_for' => 'ka0',
'sublist' => [
{
'label' => 'Statlig folketelling',
'label_for' => 'ka0kt0',
'name' => 'kt[]',
'id' => 'ka0kt0'
}
],
'name' => 'ka[]',
'id' => 'ka0>',
'Text' => 'Folketellinger'
},
{
'sublist' => [
{
'label' => 'Manntall',
'name' => 'kt[]',
'label_for' => 'ka1kt0',
'id' => 'ka1kt0'
}
],
'label_for' => 'ka1',
'id' => 'ka1>',
'name' => 'ka[]',
'Text' => 'Manntall'
},
....
{
'label_for' => 'r0',
'sublist' => [
{
'label_for' => 'r0f0',
'id' => 'r0f0',
'name' => 'f[]',
'label' => "01 Østfold"
}
],
'id' => 'r0',
'name' => 'r[]',
'Text' => "Østlandet"
},
{
'Text' => "Sørlandet",
'id' => 'r1',
'sublist' => [
{
'label_for' => 'r1f0',
'name' => 'f[]',
'id' => 'r1f0',
'label' => '09 Aust-Agder'
}
],
'label_for' => 'r1',
'name' => 'r[]'
}
]
};
I' have 2 issues I need to fix. First, I only want to get data for inputs having 'name' = ka[] (at top level).
Second, I only get data for first ul.sublist1 (If you study the page I'm scraping you can see that several "Kildekategori" have subsets of data, which are revealed if expanded/ clicked upon. Putting brackets on Text[] only gets me the sublist textnames, but not their attributes.
I'm thinking I might have to grab data in 2 scrapes instead, since nested values are revealed by id and label_for.
Solved it by scraping three times, foreach "level"
#!/usr/bin/perl
use strict;
use warnings;
use URI;
use Web::Scraper;
use Data::Dumper::Simple;
my %site;
my #res;
my $i;
my $j;
my $label_for;
my #scrape;
$site{'siteID'} = 1;
$site{'url'} = "http://digitalarkivet.arkivverket.no/finn_kilde";
$site{'name'} = "finn_kilde";
open FIL, ">$site{'name'}.csv" or die $!;
my $seperator=";";
$scrape[0] = scraper {
process 'div.listGroup.open > ul.grouped > li.expandable', 'data[]' => scraper {
process 'input',
'id' => '#id',
'value' => '#value',
'type' => '#type',
'name' => '#name';
process 'label', 'label_for' => '#for';
process 'span.listExpander ', 'text' => 'TEXT';
};
};
$scrape[1] = scraper {
process 'ul.sublist1 > li', 'data[]' => scraper {
process 'input',
'id' => '#id',
'value' => '#value',
'type' => '#type',
'name' => '#name';
process 'label', 'label_for' => '#for';
process 'span', 'text' => 'TEXT';
}
};
$scrape[2] = scraper {
process 'ul.sublist2 > li', 'data[]' => scraper {
process 'input',
'id' => '#id',
'value' => '#value',
'type' => '#type',
'name' => '#name';
process 'label', 'label_for' => '#for';
process 'span', 'text' => 'TEXT';
}
};
for $i (0 .. $#scrape){
$res[$i] = $scrape[$i]->scrape(URI->new($site{'url'}));
unless ($i) {
print FIL join($seperator,"label_for","text","name","value","id","type")."\n";
}
for $j (0 .. $#{$res[$i]->{data}}) {
if (defined($res[$i]->{data}[$j]->{label_for})){
$label_for=$res[$i]->{data}[$j]->{label_for};
} else {
$label_for="";
}
if (length($label_for)>0) {
my $name=$res[$i]->{data}[$j]->{name};
my $text=$res[$i]->{data}[$j]->{text};
my $value=$res[$i]->{data}[$j]->{value};
my $id=$res[$i]->{data}[$j]->{id};
my $type=$res[$i]->{data}[$j]->{type};
my #row=($label_for,$text,$name,$value,$id,$type);
print FIL join($seperator,#row);
print FIL "\n";
}
}
sleep(2);
}
close FIL;
print Dumper(\#res);
1;

How to access individual elements of perl Hashed object?

I'm a non-programmer attemting to retrieve useful info from our InfoBlox DHCP boxes. I've installed the Perl API and can make some use of it.
I've got an output from the Data::Dumper "thingie" that appears to have some of the info I want. I'd like to directly reference some of that data but I'm unsure how.
print Dumper(\$object)
Here is part of the Data::Dumper output;
$VAR1 = \bless( {
'network' => '10.183.1.0/24',
'override_lease_scavenge_time' => 'false',
'enable_ifmap_publishing' => 'false',
'low_water_mark_reset' => '10',
'use_lease_time' => 0,
'use_enable_option81' => 0,
'network_container' => '/',
'override_ddns_ttl' => 'false',
'rir' => 'NONE',
'network_view' => bless( {
<snip> --------------------------------------
'extattrs' => {
'Use' => bless( {
'value' => 'Voip'
}, 'Infoblox::Grid::Extattr' )
},
<snip> --------------------------------------
'members' => [
bless( {
'ipv4addr' => '10.85.9.242',
'name' => 'ig3-app3.my.net'
}, 'Infoblox::DHCP::Member' ),
bless( {
'ipv4addr' => '10.85.9.210',
'name' => 'ig3-app1.my.net'
}, 'Infoblox::DHCP::Member' ),
bless( {
'ipv4addr' => '10.85.9.226',
'name' => 'ig3-app2.my.net'
}, 'Infoblox::DHCP::Member' )
],
'override_ignore_client_identifier' => 'false',
'email_list' => undef,
'rir_registration_status' => '??
}, 'Infoblox::DHCP::Network' );
How do I view the elements? ie ...
print $object{members->name};
print $object{members->ipv4addr};
print $object{extattrs->Use->value};
I've found the API dox insufficiant for my skill level:) The data I'd like to pull remains just out of reach.
my #retrieved_objs = $session->search (
object => "Infoblox::DHCP::Network",
network => '.*\.*\.*\..*',
);
foreach $object ( #retrieved_objs ) {
my $network = $object->network;
my $comment = $object->comment;
my $extattrs = $object->extattrs;
my $options = $object->options;
print $network, " network ", $comment, " ", $extattrs, " ", $options, "\n";
}
-------- output ---
10.183.2.0/24 network HASH(0x6a2f038) ARRAY(0x1d20eb0)
10.192.1.0/24 network HASH(0x9df6540) ARRAY(0x9df5468)
10.192.2.0/24 network HASH(0xa088fc8) ARRAY(0xa089718)
You shouldn't try to access the internal values of an object directly. The module - in this case Infoblox::DHCP::Network will provide methods that allow you to read or manipulate the values properly.