I have a perl HOH (hash of hash) wherein i have serial numbers 1,2,3.... so on and directory paths with associated counter values. Could anyone please suggest a way in which i can merge the duplicate internal paths (e.g, /usr/lib , /bin/ ) to a form a new hash with addition of the values?
Sample HOH:
$VAR1 = {
'1' => {
'/usr/lib' => 18
},
'3' => {
'/bin/' => '3'
},
'4' => {
'/usr/lib' => 12
},
'2' => {
'/bin/' => '6'
},
'5' => {
'/dev/' => '2'
},
'6' => {
'/tmp/' => '8'
}
};
Final output requirement i am looking for is a simple hash with combined values. No need of serial numbers:
$VAR1 = {
'/usr/lib' => '30',
'/bin/' => '9',
'/dev/' => '2',
'/tmp/' => '8'
};
Try
sub aggregate_counts {
my $HOH = shift;
my %out;
for my $h (values %$HOH) {
for my $k ( keys %$h) {
$out{$k} += $h->{$k};
}
}
\%out
}
Use this subroutine like aggregate_counts($hoh) to get hash reference in required format.
Complete Script for testing:
use strict;
use warnings;
sub aggregate_counts {
my $HOH = shift;
my %out;
for my $h (values %$HOH) {
for my $k ( keys %$h) {
$out{$k} += $h->{$k};
}
}
\%out
}
my
$VAR1 = {
'1' => {
'/usr/lib' => 18
},
'3' => {
'/bin/' => '3'
},
'4' => {
'/usr/lib' => 12
},
'2' => {
'/bin/' => '6'
},
'5' => {
'/dev/' => '2'
},
'6' => {
'/tmp/' => '8'
}
};
use Data::Dumper;
print Dumper(aggregate_counts($VAR1))
Output:
$VAR1 = {
'/dev/' => '2',
'/tmp/' => '8',
'/bin/' => 9,
'/usr/lib' => 30
};
This could help you:
use strict; use warnings;
use Data::Dumper;
my %hash = (
'1' => {
'/usr/lib' => 18
},
'3' => {
'/bin/' => '3'
},
'4' => {
'/usr/lib' => 12
},
'2' => {
'/bin/' => '6'
},
'5' => {
'/dev/' => '2'
},
'6' => {
'/tmp/' => '8'
}
);
my %result;
foreach my $key (keys %hash){
foreach my $inner (keys %{$hash{$key}}) {
$result{$inner} += $hash{$key}{$inner};
}
}
print Dumper(\%result);
Output:
$VAR1 = {
'/usr/lib' => 30,
'/bin/' => 9,
'/dev/' => 2,
'/tmp/' => 8
};
Note: Please post your code as well in the question while asking for a help in SO.
You don't care about the keys of the outer hash. So let's start with values(%$VAR1):
{ '/usr/lib' => 18 },
{ '/bin/' => '3' },
{ '/usr/lib' => 12 },
{ '/bin/' => '6' },
{ '/dev/' => '2' },
{ '/tmp/' => '8' },
Hashes are great for grouping. We're going to iterate over the above list, then we're going to iterates over the elements of each of those hashes, using a hash to group them.
my %grouped;
for my $inner (values(%$VAR1)) {
for my $key (keys(%$inner)) {
my $val = $inner->{$key};
$grouped{$key} += $val;
}
}
And we're already done.
Related
I have some data in hashref format. I fetch data from graph.facebook.com
How to loop access?
$var = \{
'data' => [
{
'id' => '312351465029_10154168935475030',
'name' => 'Timeline Photos 1'
},
{
'name' => 'Bangchak\'s cover photo',
'id' => '312351465029_10154168087455030',
},
{
'id' => '312351465029_10154168081875030',
'name' => 'Timeline Photos 2',
}
],
'paging' => {
'previous' => 'https://graph.facebook.com/v2.6/312351465029/2',
'next' => 'https://graph.facebook.com/v2.6/312351465029/3'
}
};
These code Didn't work.
foreach $m ($var->{data})
{
if ( $m->{name} =~ /Timeline/i )
{
print "id = $m->{id}\n";
}
}
You need to dereference the array (perldoc perldsc):
use warnings;
use strict;
my $var = {
'data' => [
{
'id' => '312351465029_10154168935475030',
'name' => 'Timeline Photos 1'
},
{
'name' => 'Bangchak\'s cover photo',
'id' => '312351465029_10154168087455030',
},
{
'id' => '312351465029_10154168081875030',
'name' => 'Timeline Photos 2',
}
],
'paging' => {
'previous' => 'https://graph.facebook.com/v2.6/312351465029/2',
'next' => 'https://graph.facebook.com/v2.6/312351465029/3'
}
};
foreach my $m (#{ $var->{data} }) {
if ( $m->{name} =~ /Timeline/i )
{
print "id = $m->{id}\n";
}
}
__END__
id = 312351465029_10154168935475030
id = 312351465029_10154168081875030
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);
I have a hash that is multilevel hash i want to convert that into single level .But i am not able to do that
Actual Hash:
'MainSlab' => {
'A1' => {
'Slab' => {
'49_168' => {
'Amt' => '3000',
'Start' => '49',
'End' => '168'
},
'169_720' => {
'Amt' => '3000',
'Start' => '169',
'End' => '720'
},
'2_48' => {
'Amt' => '3000',
'Start' => '2',
'End' => '48'
},
'721_-' => {
'Amt' => '3000',
'Start' => '721',
'End' => '-'
}
}
},
'A2' => {
'Slab' => {
'49_168' => {
'Amt' => '3000',
'Start' => '49',
'End' => '168'
},
'169_720' => {
'Amt' => '4000',
'Start' => '169',
'End' => '720'
},
'2_48' => {
'Amt' => '5000',
'Start' => '2',
'End' => '48'
},
'721_-' => {
'Amt' => '3000',
'Start' => '721',
'End' => '-'
}
}
}
}
I want to convert that into simple and single level hash like this :
slab =>{
"49_168"=>{"A1"=> "3000","A2"=>"3000"},
"169_720"=>{"A1"=>"4000","A2"=>"4000"},
"2_48"=>{"A1"=>"5000","A2"=>"5000"},
"721_"=>{"A1"=>"3000","A2"=>"3000"}
}
Please help me to do this how can we do this
Assuming
my %hash = (
'MainSlab' => {
'A1' => {
'Slab' => {
'49_168' => {
'Amt' => '3000',
'Start' => '49',
'End' => '168'
},
'A2' => ...
);
Then:
my $hashref = $hash{'MainSlab'};
my $new_hashref = {};
foreach my $ax (keys %$hashref) {
foreach my $k (keys %{$hashref->{$ax}{'Slab'}}) {
$new_hashref->{$k}{$ax} = $hashref->{$ax}{'Slab'}{$k}{'Amt'};
}
}
my %new_hash = (slab => $new_hashref);
Will produce:
$new_hash = ( 'slab' => {
'49_168' => {
'A1' => '3000',
'A2' => '3000'
},
'169_720' => {
'A1' => '3000',
'A2' => '4000'
...
);
use Data::Dumper qw();
## actual hash $h1
my $h1 = { 'MainSlab' => { 'A1' => { 'Slab' => { '49_168' => { 'Amt' => '3000', 'Start' => '49', 'End' => '168' }, '169_720' => { 'Amt' => '3000', 'Start' => '169', 'End' => '720' }, '2_48' => { 'Amt' => '3000', 'Start' => '2', 'End' => '48' }, '721_-' => { 'Amt' => '3000', 'Start' => '721', 'End' => '-' } } }, 'A2' => { 'Slab' => { '49_168' => { 'Amt' => '3000', 'Start' => '49', 'End' => '168' }, '169_720' => { 'Amt' => '4000', 'Start' => '169', 'End' => '720' }, '2_48' => { 'Amt' => '5000', 'Start' => '2', 'End' => '48' }, '721_-' => { 'Amt' => '3000', 'Start' => '721', 'End' => '-' } } } } };
## transform to $h2
my #l2 = keys(%{$h1->{'MainSlab'}});
my #l1 = keys(%{$h1->{'MainSlab'}->{$l2[0]}->{'Slab'}});
my $h2 = {};
foreach my $l1 (#l1) {
my $inner = {};
foreach my $l2 (#l2) {
$inner->{$l2} = $h1->{'MainSlab'}->{$l2}->{'Slab'}->{$l1}->{'Amt'};
} ## end foreach
$h2->{'slab'}->{$l1} = $inner;
} ## end foreach
## print result
print(Data::Dumper->Dump([$h2],['$h2']));
Output:
$h2 = {
'slab' => {
'49_168' => {
'A1' => '3000',
'A2' => '3000'
},
'169_720' => {
'A1' => '3000',
'A2' => '4000'
},
'2_48' => {
'A1' => '3000',
'A2' => '5000'
},
'721_-' => {
'A1' => '3000',
'A2' => '3000'
}
}
};
I want to create url from input hash ref. Suppose I give hash ref as
my $input_hash_ref = {
'1' => 'A',
'2' => 'B',
'3' => {
'4' => {
'5' => {
'6' => [
'ice cream','drink'
],
'7' => 'large'
}
'8' => 'perl'
'9' => 'rosy'
},
'10'=>'june'
},
};
Then this is to be converted as
1=A&2=B&3.4.5.6=ice cream|drinks&3.4.5.7=large&3.8=perl&3.9=rosy&10=june
Help needed.
I just have to say, you shouldn't be composing query strings with a naive implementation with join.
use URI;
use URI::QueryParam;
my $u = URI->new("","http");
Then you can simply:
$u->query_param_append("1" => "A", "2" => "B", ....);
Or even
$u->query_form_hash( %somedata );
Note that this does not automatically deal with your custom schema for serialized nesting, but it does guarantee that you'll emit a valid query string that any server will understand.
Though you can also use a Perl Module to convert from a deeply-nested Hash to a Flat hash and back again:
Data::SplitSerializer
And you can use this to convert between formats on both sides.
Example usage:
use strict;
use warnings;
use utf8;
use Data::SplitSerializer;
use Data::Dump qw(pp);
use URI;
use URI::QueryParam;
my $input_hash = {
'1' => 'A',
'2' => 'B',
'3' => {
'4' => {
'5' => {
'6' => [
'ice cream','drink'
],
'7' => 'large'
}
},
},
'8' => 'june',
'9' => "Challenging & Value",
};
my $flattened = Data::SplitSerializer->new( path_style => 'DZIL' )->serialize($input_hash);
pp $flattened;
my $uri = URI->new("http://example.com/thing?");
$uri->query_form_hash( $flattened );
printf "%s\n", $uri;
my $copy = URI->new( $uri . "" ); # simulate getting it server side
my $copy_hash = $copy->query_form_hash;
pp $copy_hash;
my $deep = Data::
SplitSerializer->new( path_style => 'DZIL' )->deserialize($copy_hash);
pp $deep;
Example Output:
{
"1" => "A",
"2" => "B",
"3.4.5.6[0]" => "ice cream",
"3.4.5.6[1]" => "drink",
"3.4.5.7" => "large",
"8" => "june",
"9" => "Challenging & Value",
}
http://example.com/thing?9=Challenging+%26+Value&3.4.5.6%5B1%5D=drink&2=B&8=june&3.4.5.6%5B0%5D=ice+cream&1=A&3.4.5.7=large
{
"1" => "A",
"2" => "B",
"3.4.5.6[0]" => "ice cream",
"3.4.5.6[1]" => "drink",
"3.4.5.7" => "large",
"8" => "june",
"9" => "Challenging & Value",
}
{
1 => "A",
2 => "B",
3 => { 4 => { 5 => { 6 => ["ice cream", "drink"], 7 => "large" } } },
8 => "june",
9 => "Challenging & Value",
}
use URI::Escape;
sub serial {
my ($h, $p) = #_;
return join "&", map {
my $v = $h->{$_};
my $ref = ref($v);
my $isH = $ref eq "HASH";
my $pp = join ".", grep defined, $p, $_;
$v = $isH ? serial($v,$pp)
: $ref ? join("|", map uri_escape($_), #$v)
: uri_escape($v);
$isH ? $v : "$pp=$v";
}
sort keys %$h;
}
my $input_hash_ref = {
'1' => 'A',
'2' => 'B',
'3' => {
'4' => {
'5' => {
'6' => [
'ice cream','drink'
],
'7' => 'large'
}
},
},
'8' => 'june'
};
print serial($input_hash_ref);
output
1=A&2=B&3.4.5.6=ice cream|drink&3.4.5.7=large&8=june
sub c {
my ($v, $p) = #_;
my $r = ref($v);
return map { c($v->{$_}, $p ? $p . '.' . $_ : $_) } keys(%$v) if $r eq 'HASH';
return $p . '=' . join('|', #$v) if $r eq 'ARRAY';
return $p . '=' . $v;
}
say(join('&', c($input_hash_ref)));
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 = ( ... );