Extracting specific elements from hash - perl

my %hash = {
'student1' => {
'Name' => 'aaa',
'Age' => '20',
'Subjects' => ['Maths','Science']
},
'student2' => {
'Name' => 'bbb',
'Age' => '22',
'Subjects' => ['English','Science']
}
}
my $hashRef = \%hash;
how do i extract the second subject name from this using hashref ?

Your declaration of %hash is incorrect, do this instead:
my %hash = (
'student1' => {
'Name' => 'aaa',
'Age' => '20',
'Subjects' => ['Maths','Science']
},
'student2' => {
'Name' => 'bbb',
'Age' => '22',
'Subjects' => ['English','Science']
}
);
Note the parens instead og brace.
Then to get the second subject :
say $hashRef->{student1}{Subjects}[1];

Your code is wrong, { } creates a hashref and you are storing it in a hash. You should do:
my %hash = (
'student1' => {
'Name' => 'aaa',
'Age' => '20',
'Subjects' => ['Maths','Science']
},
'student2' => {
'Name' => 'bbb',
'Age' => '22',
'Subjects' => ['English','Science']
}
);
my $hashRef = \%hash;
or even better:
my $hashref = {
student1 => { ... },
student2 => { ... },
};
Then you can access with:
$hashRef->{student2}->{Subjects}[1]

Subjects are an array reference inside a hash inside a hash.
$hashRef->{student1}{Subjects}[1]
Also, do not use curly brackets to initilize a hash, they create an anonymous hash. Use round parentheses:
my %hash = ( ... );

Related

Perl dynamic hash of hash errors

I am looping over a query and returning results. I am trying to add a hash to another hashes. But running into trouble.
my %users_data;
while($sth->fetch)
{
$mygroup =>
{
'fname' => $fname,
'lname' => $lname,
'address' =>
{
'street' => $street,
'city' => $city,
},
'id' => $uid,
},
}
how do I add the $mygroup hash to %users_data hash?
long hand would be.
my %users_data = (
'salesmanager' =>
{
'fname' => 'mike',
'lname' => 'john',
'address' =>
{
'street' => '123 street',
'city' => 'Brooklyn',
},
'id' => 12,
},
'garagemanager' =>
{
'fname' => 'Mark',
'lname' => 'Jones',
'address' =>
{
'street' => '355 street',
'city' => 'Brooklyn',
},
'id' => 13,
},
)
Simply access the target key and assign its new value:
my %users_data;
while($sth->fetch)
{
$users_data{$mygroup} = {
'fname' => $fname,
'lname' => $lname,
'address' =>
{
'street' => $street,
'city' => $city,
},
'id' => $uid,
};
}

Accessing Specific OTRS Dynamic Field value via SOAP

How do I further access this dynamic field value? Upon using below dumper,
print Dumper( $Body->{$ResponseKey} );
The result is :
$VAR1 = {
'Ticket' => {
'Title' => 'TPLUS Service PIC',
'DynamicField' => [
{
'Value' => '43312',
'Name' => 'BugID'
},
{
'Value' => '6',
'Name' => 'OTRSMV'
},
{
'Value' => '6.13',
'Name' => 'OTRSPLV'
},
{
'Value' => 'Dev',
'Name' => 'OTRSUse'
},
{
'Value' => '2018-03-02 00:28:00',
'Name' => 'RefDate'
},
{
'Value' => '0',
'Name' => 'RefNumber'
},
{
'Value' => '',
'Name' => 'StartTime'
}
],
'StateType' => 'open',
'SLAID' => ''
}
};
How can I access the single value of DynamicField->RefDate ? Thanks
my $fields = $Body->{$ResponseKey}{Ticket}{DynamicField};
my ($ref_date) =
map $_->{Value},
grep $_->{Name} eq 'RefDate',
#$fields;
or
my %fields;
$fields{ $_->{Name} } = $fields{ $_->{Value} }
for #{ $Body->{$ResponseKey}{Ticket}{DynamicField} };
my $ref_date = $fields{RefDate};

Accessing xml data using perl XML::Simple

I have some xml data, the dump looks like this:
$VAR1 = {
'Members' => [
{
'Age' => '19',
'Name' => 'Bob'
},
{
'Age' => '18',
'Name' => 'Jane'
},
{
'Age' => '21',
'Name' => 'Pat'
},
{
'Age' => '22',
'Name' => 'June'
}
],
'Sports' => [
{
'Players' => '20',
'Name' => 'Tennis'
},
{
'Players' => '35',
'Name' => 'Basketball'
}
],
};
I have tried the following code to print out the data:
foreach my $member (#($xml->{Members})) {
print("Age: $xml->{Age}");
}
But keep getting errors like:
Can't use string ("4") as a HASH ref while "strict refs" in use
Any idea why this won't work?
You are using the wrong syntax.
# here ... and here
# V V
foreach my $member (#($xml->{Members})) { ... }
To dereference, you need curly braces {}, not parenthesis ().
Once you've fixed that (which I think was a typo in the question, not in your real code), you have:
foreach my $member ( #{ $xml->{Members} } ) {
print "Age: $xml->{Age}";
}
But that's still wrong. You want to access the $member, not the whole $xml structure, because that doesn't have an Age, does it?
foreach my $member ( #{ $xml->{Members} } ) {
print "Age: $member->{Age}\n";
}
That will give you
Age: 19
Age: 18
Age: 21
Age: 22

Convert a flat datastructure into a tree

I have an array of hashes. Each element in the array is a node in a hierarchical tree and has referential data for who the parent is. I will have thousands and hundreds of thousands of nodes in the tree... essentially an unknown set of nodes has to be converted to JSON (shown below) for use with http://bl.ocks.org/robschmuecker/7880033
UPDATE: position_id is a node in the heretical tree. placement_id is the parent's position_id (adjacency referential tree).
UPDATE: Here's the full AoH Data::Dumper result with Nested Set and Adjacency result from a modified version of DBIx::Tree::NestedSet (custom).
$VAR1 = [
{
'lft' => '673',
'id' => '109',
'date_created' => '2015-08-15',
'level' => '7',
'user_id' => '13',
'placement_id' => '11',
'position_id' => '13',
'status' => '1',
'structure_id' => '1',
'rght' => '684'
},
{
'placement_id' => '13',
'position_id' => '22',
'status' => '1',
'structure_id' => '1',
'rght' => '679',
'lft' => '674',
'date_created' => '2015-08-15',
'id' => '116',
'level' => '8',
'user_id' => '22'
},
{
'user_id' => '101',
'level' => '9',
'id' => '200',
'date_created' => '2015-08-15',
'lft' => '675',
'rght' => '676',
'structure_id' => '1',
'status' => '1',
'position_id' => '101',
'placement_id' => '22'
},
{
'date_created' => '2015-08-15',
'id' => '201',
'level' => '9',
'user_id' => '374',
'lft' => '677',
'structure_id' => '1',
'rght' => '678',
'placement_id' => '22',
'position_id' => '374',
'status' => '1'
},
{
'lft' => '680',
'user_id' => '95',
'level' => '8',
'id' => '117',
'date_created' => '2015-08-15',
'status' => '1',
'position_id' => '95',
'placement_id' => '13',
'rght' => '681',
'structure_id' => '1'
}
];
THIS IS THE GOAL, For this example I need to end up with:
{
"name": "13",
"children": [
{
"name": "22",
"children": [
{
"name": "101"
},
{
"name": "374"
}
]
},
{
"name": "95"
}
]
}
You can also see the format I am trying to arrive at here (minus size):
http://bl.ocks.org/robschmuecker/7880033#flare.json
My failed approach(es) included various attempts at looping through the array of hashes to create a recursive Hash of Hashes that can then be used with the JSON Perl module to create the actual JSON I need.
my $data = [
{ position_id => 123, placement_id => undef },
{ position_id => 456, placement_id => 123 },
{ position_id => 789, placement_id => 123 },
# ...
];
my $roots;
{
my %recs_by_name;
my %children_by_parent_name;
for my $row (#$data) {
my $name = $row->{position_id};
my $parent_name = $row->{placement_id};
my $rec = {
name => $name,
};
push #{ $children_by_parent_name{$parent_name // 'root'} }, $rec;
$recs_by_name{$name} = $rec;
}
$roots = delete($children_by_parent_name{root}) || [];
for my $name (keys(%children_by_parent_name)) {
my $children = $children_by_parent_name{$name};
if ( my $rec = $recs_by_name{$name} ) {
$rec->{children} = $children;
} else {
die("Parent $name doesn't exist.\n");
push #$roots, #$children;
}
}
}
print(Dumper($roots));
Tested.
You appear to have the depth of each node available to you (level). Simpler code could be used if your data was sorted by increasing depths.
While it was #ikegami who ultimately answered the question that led to the solution. I believe the following adaptation adds 4 important elements/clarifications I found helpful, and thought others reading this question and answer would also find useful.
1- Clear addition of all key,value pairs from the originating AoH to the resulting HOH. See while loop.
2- A Child node counter.
3- Inclusion and use of the encode_json function from JSON
4- The result is also an Array with a Hash as the first element. Newbies (like me) might find the explicit #{$roots}[0] passed to encode_json as helpful.
At first I had a similar adapted solution posted as an UPDATE within my question, but was admonished that it was bad etiquette and instructed to post an answer.
#ikegami's deserves the credit for the core of the solution.
sub get_jsonTree {
my ($array_of_hashes_ref) = #_;
my $roots;
my %recs_by_name;
my %children_by_parent_name;
my %count;
for my $row (#$array_of_hashes_ref) {
my $name = $row->{position_id};
my $parent_name = $row->{placement_id};
my $rec = {
name => $name,
};
## Added to loop through all key,value pairs and add them to $rec
while ( my ($key, $value) = each(%$row) ) {
$rec->{$key} = $value;
}
##Added To Count Child Nodes
$count{$parent_name} = 0 if (!$count{$parent_name});
$rec->{'child_count'} = $count{$parent_name};
$count{$parent_name}++;
push #{ $children_by_parent_name{$parent_name // 'root'} }, $rec;
$recs_by_name{$name} = $rec;
}
$roots = delete($children_by_parent_name{root}) || [];
for my $name (keys(%children_by_parent_name)) {
my $children = $children_by_parent_name{$name};
if ( my $rec = $recs_by_name{$name} ) {
$rec->{children} = $children;
} else {
$util{'test'} .= "Parent $name doesn't exist.\n<BR>";
push #$roots, #$children;
}
}
use JSON;
my $json_str = encode_json(#{$roots}[0]);
return $json_str;
}
my $array_of_hashes_ref = [
{ position_id => 123, placement_id => undef },
{ position_id => 456, placement_id => 123 },
{ position_id => 789, placement_id => 123 },
# ...
];
my $json_str = &get_jsonTree($array_of_hashes_ref);

How to choose which is the reference in Data::Dumper

Let's consider this example:
#!/usr/bin/env perl
use strict;
use Data::Dumper;
my $node = node(undef, undef, 'root');
my $root = $node;
$node = node($root, $node, 'tom');
push $root->{children}, $node;
$node = node($root, $node, 'clarence');
push $root->{children}, $node;
Data::Dumper::Purity;
#$root->{children}[0]->{younger} = $root->{children}[1];
print Dumper $root;
sub node {
return {
parent => shift,
prev => shift,
name => shift,
children => [],
};
}
Which gives this output:
$VAR1 = {
'parent' => undef,
'prev' => undef,
'name' => 'root',
'children' => [
{
'parent' => $VAR1,
'prev' => $VAR1,
'name' => 'tom',
'children' => []
},
{
'parent' => $VAR1,
'prev' => $VAR1->{'children'}[0],
'name' => 'clarence',
'children' => []
}
]
};
We can clearly see that root has 2 children named tom and clarence. The reference of clarence on tom is really clear $VAR1->{'children'}[0].
However, if I add a reference on tom to clarence with $root->{children}[0]->{younger} = $root->{children}[1];, the output get messed up:
$VAR1 = {
'parent' => undef,
'prev' => undef,
'name' => 'root',
'children' => [
{
'parent' => $VAR1,
'prev' => $VAR1,
'younger' => {
'parent' => $VAR1,
'prev' => $VAR1->{'children'}[0],
'name' => 'clarence',
'children' => []
},
'name' => 'tom',
'children' => []
},
$VAR1->{'children'}[0]{'younger'}
]
};
Is there any possibility to constraint Data::Dumper or any other dumper to always consider some keys as references in order to properly display a tree?
There isn't a lot you can do because Data::Dumper scans structures depth-first, but I suggest that you use
$Data::Dumper::Deepcopy = 1
which will duplicate hash values in the output instead of inserting cross-references. This is the result
$VAR1 = {
'prev' => undef,
'children' => [
{
'parent' => $VAR1,
'name' => 'tom',
'younger' => {
'children' => [],
'prev' => $VAR1->{'children'}[0],
'name' => 'clarence',
'parent' => $VAR1
},
'prev' => $VAR1,
'children' => []
},
{
'children' => [],
'prev' => {
'parent' => $VAR1,
'name' => 'tom',
'younger' => $VAR1->{'children'}[1],
'prev' => $VAR1,
'children' => []
},
'name' => 'clarence',
'parent' => $VAR1
}
],
'parent' => undef,
'name' => 'root'
};