Iterating over an array reference - perl

# data $bug
{ 
'keyword_objects' => [
bless( { 'id' => 15, 'name' => 'CRASH'}, 'SomeModule::SomeFilename' ),
bless( { 'id' => 6, 'name' => 'CUSTOMER' }, 'SomeModule::SomeFilename' ) ],
'category' => 'error'
}
foreach my $keyword ($bug->{keyword_objects}) {
print Dumper($keyword);
}
It prints the whole of keyword_objects instead of the individual items in it. Now it should be obvious to you that I know so little about Perl, I'd like to also ask what is the right way to reference name in each keyword.

To iterate over the elements in your array ref, you need to dereference it. foreach needs a list.
foreach my $keyword ( #{ $bug->{keyword_objects} } ) {
Your objects are hash references, so you could reach into their internals like this:
$keyword->{name}
However, messing with internals is not a good idea. Instead, you should write accessors and call them as a method.
$keyword->name

Related

How to address specific array element in complex data structure in 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,
},
],
}

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.

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

How can I join a nested Perl hash?

I have a Perl hash, where I store information about LUNs. It has the following structure:
my %luns = (
360000 => {
Devices => [
{ Major_Minor => "8:144",
SCSI_Address => "1:0:0:8",
SCSI_Device => "sdj",
SCSI_Host => "host1",
},
{ Major_Minor => "129:48",
SCSI_Address => "3:0:0:8",
SCSI_Device => "sder",
SCSI_Host => "host3",
},
],
DM_Device => "dm-13",
Size => "45G",
WWID => 360000,
},
360001 => {
Devices => [
{ Major_Minor => "70:144",
SCSI_Address => "1:0:1:39",
SCSI_Device => "sddb",
SCSI_Host => "host1",
},
{ Major_Minor => "135:48",
SCSI_Address => "3:0:1:39",
SCSI_Device => "sdij",
SCSI_Host => "host3",
},
],
DM_Device => "dm-53",
Size => "200G",
WWID => 360000,
},
);
How can I use join to get a comma-separated list of all SCSI_Devices, for example, of 360000?
You're working with a Hash of Hash of Array of Hash. To learn how to work with such structures, I recommend reading perldsc - Perl Data Structures Cookbook.
In this instance, the following loop will print out each of your device lists:
for my $id ( sort { $a <=> $b } keys %luns ) {
my #devices = map { $_->{SCSI_Device} } #{ $luns{$id}{Devices} };
print "$id - #devices\n";
}
Outputs:
360000 - sdj sder
360001 - sddb sdij
Live Demo
You say you want a list of values for LUN 360000, so for a start you need
$luns->{36000}
which is another hash with a Devices element, which has an array reference as a value, and DM_Device, Size, and WWID elements, whose values are simple scalars.
So presumably you want the list that is
$luns->{36000}{Devices}
which is an array of references to hashes, each of which has Major_Minor, SCSI_Address, SCSI_Device, and SCSI_Host elements.
It sounds like you want the SCSI_Device element, and map is the ideal tool to help you with this
my #scsi_devices = map { $_->{SCSI_Device} } #{ $luns->{360000}{Devices} };
That last step is a big leap, and it may help to separate it in your code. For instance, you can copy the reference to the list of devices for 360000, like this
my $devices = $luns->{360000}{Devices};
and extract the SCSI_Device from each of the hashes in that array with
my #scsi_devices = map { $_->{SCSI_Device} } #$devices;
Either way, the array reference must be dereferenced and the required element from each hash in that array must be extracted.
To get a CSV record, unless the data may contain commas of double-quotes, you simply need to join the result of that map
print join(',', #scsi_devices), "\n";
output
sdj,sder
Although I think this falls short of what you actually need. If this isn't clear then please ask.

Iterating over bless objects in Perl

I'm working on some code to query F5 load balancers using the BigIP::iControl module.
Right now, I'm getting the following output when doing a Dumper on a variable I get back from a particular function.
I've having a lot of trouble iterating of this object.
How could I go about iterating over this and only taking out the monitor_status for each member?
$VAR1 = [
bless( [
bless( {
'monitor_status' => 'MONITOR_STATUS_UP',
'member' => bless( {
'address' => '127.0.0.0.1',
'port' => '8085'
}, 'Common::IPPortDefinition' )
}, 'LocalLB::PoolMember::MemberMonitorStatus' ),
bless( {
'monitor_status' => 'MONITOR_STATUS_UP',
'member' => bless( {
'address' => '127.0.0.0.1',
'port' => '8085'
}, 'Common::IPPortDefinition' )
}, 'LocalLB::PoolMember::MemberMonitorStatus' ),
bless( {
'monitor_status' => 'MONITOR_STATUS_DOWN',
'member' => bless( {
'address' => '127.0.0.0.1',
'port' => '8085'
}, 'Common::IPPortDefinition' )
}, 'LocalLB::PoolMember::MemberMonitorStatus' ),
bless( {
'monitor_status' => 'MONITOR_STATUS_DOWN',
'member' => bless( {
'address' => '127.0.0.0.1',
'port' => '8085'
}, 'Common::IPPortDefinition' )
}, 'LocalLB::PoolMember::MemberMonitorStatus' )
], 'LocalLB::PoolMember::MemberMonitorStatus[]' )
];
I'm not sure whether those member variables are public - I'm not familiar with the modules used - so this might well violate the encapsulation of the LocalLB::PoolMember::MemberMonitorStatus class. You should check before using.
for my $mms ( #{$VAR1->[0]} ) {
warn $mms->{monitor_status};
}
It would be better to check whether the MemberMonitorStatus class provides an accessor, and possibly an iterator for the member monitor status array.
The above was tested simply by pasting your Dumper output into a Perl script with the code of the for loop implemented based on eyeballing the data structure.
(edit: based on F5 webcentral docs in the Google cache, it may be that MemberMonitorStatus is a simple struct in the underlying code, exposed in the Perl as a class with two member variables - but no behaviour. If so, the above is probably OK.)