Build hash of hashes from a single hash - perl

I have the following hash
my %input_hash = (
'test1' => '100',
'test2' => '200',
'test3' => '300',
'test4' => '400',
'test5' => '500'
);
What I need is to build a hash of hash from the above hash. I need to put the first 2 of the above key value pair into a key of the hash of hash. Better explained with this example.
Desired output:
my %expected_hash = (
1 => {
'test1' => '100',
'test2' => '200',
},
2 => {
'test3' => '300',
'test4' => '400',
},
3 => {
'test5' => '500'
},
);
I would like the split to be dynamic. Example,if i need to split by 3, the desired output should be
my %expected_hash = (
1 => {
'test1' => '100',
'test2' => '200',
'test3' => '300',
},
2 => {
'test4' => '400',
'test5' => '500'
},
);

Here's a version that uses splice to get a dynamic number of elements. Note that you have to sort the keys in the hash, because hashes are unordered.
use strict;
use warnings;
use Data::Dumper;
my %input_hash = (
'test1' => '100',
'test2' => '200',
'test3' => '300',
'test4' => '400',
'test5' => '500',
'test6' => '600',
'test7' => '700',
'test8' => '800',
'test9' => '900'
);
my $foo = foo(\%input_hash, 4);
print Dumper $foo;
sub foo {
my ($href, $count) = #_;
my #keys = sort keys %$href;
my %hash;
my $i = 1;
while (#keys) {
$hash{$i++} = { map { $_ => $href->{$_} }
splice #keys, 0, $count };
}
return \%hash;
}
Output:
$VAR1 = {
'1' => {
'test1' => '100',
'test4' => '400',
'test3' => '300',
'test2' => '200'
},
'3' => {
'test9' => '900'
},
'2' => {
'test8' => '800',
'test5' => '500',
'test7' => '700',
'test6' => '600'
}
};

Here's a solution using an index array built from number of keys in %input_hash and desired size set in $chunk_size:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my %input_hash = (
'test1' => '100',
'test2' => '200',
'test3' => '300',
'test4' => '400',
'test5' => '500'
);
my $chunk_size = 2;
my #indexes = map {int($_ / $chunk_size) + 1} 0 .. keys %input_hash;
my %expected_hash;
for my $key (sort keys %input_hash) {
my $index = shift #indexes;
$expected_hash{$index}{$key} = $input_hash{$key};
}
print Dumper \%expected_hash;
Output:
$VAR1 = {
'1' => {
'test1' => '100',
'test2' => '200'
},
'3' => {
'test5' => '500'
},
'2' => {
'test4' => '400',
'test3' => '300'
}
};
Of course, as TLP mentioned, you have to sort %input_hash to achieve this.

#! /usr/bin/perl -w
use strict;
my ($expected_hash_key, $cnt, $L1, $L2) = (1, 1, "", "");
my %expected_hash;
# Change $LIMIT to required value. 2 or 3.
my $LIMIT = 2;
my %input_hash = (
'test1' => '100',
'test2' => '200',
'test3' => '300',
'test4' => '400',
'test5' => '500'
);
for (sort keys %input_hash) {
$cnt++;
$expected_hash{$expected_hash_key}{$_} = $input_hash{$_};
if ($cnt == $LIMIT + 1) {
$cnt = 1;
$expected_hash_key++;
}
}
for $L1 (sort keys %expected_hash) {
print "$L1 ==> \n";
for $L2 (sort keys %{ $expected_hash{$L1} }) {
print "$L2 -> $expected_hash{$L1}{$L2}\n";
}
print "\n";
}

Related

Compare two hashes by value to get keys/values where the 2nd is greater

I have code:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
#Coder: pavel69
open FILE1, "/home/stackovershroom/0/28.12.txt" or die;
my %dec28;
while (my $line1=<FILE1>) {
chomp($line1);
(my $plu28, my $count28) = split / /, $line1;
$dec28{$plu28} = $count28;
}
open FILE2, "/home/stackovershroom/0/29.12.txt" or die;
my %dec29;
while (my $line2=<FILE2>) {
chomp($line2);
(my $plu29, my $count29) = split / /, $line2;
$dec29{$plu29} = $count29;
}
print Dumper \%dec28;
print Dumper \%dec29;
Output:
$VAR1 = {
'3203100' => '7',
'3467390' => '14',
'3017931' => '19',
'3312878' => '1.806',
'3362576' => '56',
'3173204' => '23',
'3335495' => '6.377',
'202' => '30.848',
'2161067' => '13',
'3356411' => '6',
'3483437' => '6',
'3359188' => '11',
'...' => '...' #yet more 500 strings!
};
$VAR1 = {
'3153446' => '89.480',
'2062513' => '9',
'3386209' => '8.379',
'3195682' => '17.266',
'3411129' => '18',
'3154498' => '4.916',
'2043226' => '12',
'...' => '...' #yet more 500 strings!
};
I want to compare two hashes for searching keys from %dec28, values of which were incremented (from %dec29).
For clarity, in %dec28 I have:
'209198' => '2'
in %dec29 I have:
'209198' => '13'
Need to get all (only) incremented values for %dec28 when compare %dec28 <=> %dec29 (Increment values contains in %dec29). I was only able to get new keys/values that occur in %dec29
Minimal example:
%dec28 = (
'3091212' => '1',
'2093334' => '74',
'209' => '5.600',
'1947754' => '3',
'3130087' => '6');
%dec29 = (
'3091212' => '4',
'2093334' => '60',
'209' => '13.844',
'1947754' => '9',
'3130087' => '6');
Need to construct new
%increment_values = (
'3091212' => '4'
'209' => '13.844'
'1947754' => '9');
It is possible? How I can do it?
I don't understand what you are trying to do, but there are ways to compare parallel keys in two hashes.
Go through all the keys of one hash and check if those keys are in the other hash:
foreach my $key1 ( keys %hash1 ) {
next unless exists $hash2{$key1};
... Do whatever you need to do
}
If you want the larger values, you might assign just those to a separate hash:
my %larger;
foreach my $key ( keys %hash1 ) {
next unless exists $hash2{$key};
next unless $hash2{$key} > $hash2{$key};
$larger{$key} = $hash2{$key};
}
Or, you can get the common keys without doing anything:
my #common_keys = grep { exists $hash2{$_} } keys %hash1;
You can expand the grep to be more specific:
my #larger_keys = grep {
exists $hash2{$_} && $hash2{$_} > $hash1{$_}
} keys %hash1;
Once you have the interesting keys, you can easily get a smaller hash of just the interesting pairs:
my %larger =
map { $_ => $hash2{$_) }
grep { ... }
keys %hash1;
With v5.20 or later, you can use key-value slices (reusing the keys we extracted earlier):
use v5.20;
my %larger = %hash2{ #larger_keys };
If I understand correctly, you want something like this:
# For each key in %dec28
for my $k (keys %dec28) {
# If the same key exists in %dec29
# And the %dec29 value is greater than the %dec28 value
if (exists $dec29{$k} and $dec29{$k} > $dec28{$k}) {
# Print something useful
print "$k: $dec28{$k} -> $dec29{$k}\n";
}
}
Loop through the new hash keys (dec29). If the key exists in the old hash (dec28), compare the 2 values. If the new value is greater, add it to the increment hash.
use warnings;
use strict;
use Data::Dumper;
$Data::Dumper::Sortkeys=1;
my %dec28 = (
'3091212' => '1',
'2093334' => '74',
'209' => '5.600',
'1947754' => '3',
'3130087' => '6');
my %dec29 = (
'3091212' => '4',
'2093334' => '60',
'209' => '13.844',
'1947754' => '9',
'3130087' => '6');
my %increment_values;
for my $k (keys %dec29) {
if (exists $dec28{$k}) {
if ($dec29{$k} > $dec28{$k}) {
$increment_values{$k} = $dec29{$k};
}
}
}
print Dumper(\%increment_values);
Here is a compact solution that can be a one-liner.
%day1 = ('3091212' => '1',
'2093334' => '74',
'209' => '5.600',
'1947754' => '3',
'3130087' => '6');
%day2 = ('3091212' => '4',
'2093334' => '60',
'209' => '13.844',
'1947754' => '9',
'3130087' => '6');
%incremented_values =
map { exists $day1{$_} && $day2{$_} > $day1{$_} ?
($_, $day2{$_}) : ()
} keys %day2;
Now you showed the key values as strings. If you need to preserve them as pure strings without numifying them, you'll need to copy the values.
%incremented_values =
map { exists $day1{$_} && (my $v2=$day2{$_}) > (my $v1=$day1{$_}) ?
($_, $day2{$_}) : ()
} keys %day2;
HTH

Perl: Get minimum distance value from multi hash using List::Util

i would like to get the smallest distance to a "snaffle" from the following hash:
$VAR1 = {
'0' => {
'y' => '7012',
'snaffle' => {
'5' => {
'y' => '3856',
'x' => '875',
'id' => '5',
'distance' => 9734
},
'6' => {
'x' => '10517',
'id' => '6',
'distance' => 510,
'y' => '6741'
},
'4' => {
'y' => '5291',
'id' => '4',
'x' => '11331',
'target' => 'true',
'distance' => 2125
},
'8' => {
'x' => '11709',
'id' => '8',
'distance' => 2236,
'y' => '5475'
},
'7' => {
'distance' => 8485,
'x' => '4591',
'id' => '7',
'y' => '544'
}
},
'x' => '10084',
'distance2mybase' => 10598,
'distance2enemybase' => 6755,
'type' => 'WIZARD',
'id' => '0',
'state' => 0
},
It is filled early:
# game loop
while (1) {
chomp(my $entities = <STDIN>); # number of entities still in game
for my $i (0..$entities-1) {
chomp($tokens=<STDIN>);
my ($entity_id, $entity_type, $x, $y, $vx, $vy, $state) = split(/ /,$tokens);
my $type;
if ($entity_type eq "WIZARD") {
$type = "wizard";
}
if ($entity_type eq "OPPONENT_WIZARD") {
$type = "enemy";
}
if ($entity_type eq "SNAFFLE") {
$type = "snaffle";
}
if ($entity_type eq "BLUDGER") {
$type = "bludger";
}
$entity{$type}{$entity_id}{x} = $x;
$entity{$type}{$entity_id}{y} = $y;
$entity{$type}{$entity_id}{state} = $state;
$entity{$type}{$entity_id}{id} = $entity_id;
$entity{$type}{$entity_id}{type} = $entity_type;
$entity{$type}{$entity_id}{distance2mybase} = &getdistance($entity{$type}{$entity_id}{x},$entity{$type}{$entity_id}{y},$mybase_x,$mybase_y);
$entity{$type}{$entity_id}{distance2enemybase} = &getdistance($entity{$type}{$entity_id}{x},$entity{$type}{$entity_id}{y},$enemybase_x,$enemybase_y);
}
foreach my $wizard_id (sort keys %{ $entity{'wizard'} }) {
foreach my $snaffle_id (sort keys %{ $entity{'snaffle'} }) {
$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{id} = $snaffle_id;
$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{x} = $entity{'snaffle'}{$snaffle_id}{x};
$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{y} = $entity{'snaffle'}{$snaffle_id}{y};
$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{distance} = &getdistance($entity{'wizard'}{$wizard_id}{x},$entity{'wizard'}{$wizard_id}{y},$entity{'snaffle'}{$snaffle_id}{x},$entity{'snaffle'}{$snaffle_id}{y});
}
&action($wizard_id,"sweep","up");
}
I tried List::Util::min, but i think im searching too deep, because as you can see in the output, it targets the wrong snaffle. (6 distance is lower then 4, which is the current target)
How can i find the overall minimum distance from all snaffles? (in case you wonder, its a codingame(.com))
sub snafflecheck {
my $wizard_id = shift;
my $wizard_x = shift;
my $wizard_y = shift;
if ($entity{'snaffle'}) {
foreach my $snaffle_id (sort keys %{ $entity{'snaffle'} }) {
my $snaffle_x = $entity{'snaffle'}{$snaffle_id}{x};
my $snaffle_y = $entity{'snaffle'}{$snaffle_id}{y};
my $distance2snaffle = &getdistance($wizard_x,$wizard_y,$snaffle_x,$snaffle_y);
my $nearest = min $entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{distance};
if ($distance2snaffle) {
if ($distance2snaffle == $nearest) {
$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{target} = "true";
return("true",$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{id},$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{x},$entity{'wizard'}{$wizard_id}{snaffle}{$snaffle_id}{y},$distance2snaffle);
}
}
}
}
Given the shown data, this is the list of all values for key distance
my #dist = map { $_->{distance} } values %{$entity->{0}{snaffle}};
However, getting the minimum value doesn't reveal its key.
One way of finding the key for which the value of distance is smallest
use List::Util 'reduce';
my $snaff = $entity->{0}{snaffle};
my $min_dist = reduce {
$snaff->{$a}{distance} < $snaff->{$b}{distance} ? $a : $b
} keys %$snaff;
print "Minimal distance: $snaff->{$min_dist}{distance} for key $min_dist\n";
To have more control you can instead iterate over %$snaff using each.
You can also sort the extracted $snaff by distance value, if you'd like to have them all.
You should first extract the reference to the snaffle hash to make things tidier. Then you can just use map to extract the distance field of each hash element and min to find the smallest of them.
If you want to know the snaffle with the smallest distance then
I suggest that you install List::UtilsBy and use its min_by operator
This code shows both operations
The hash is identical to your own, but expressed more compactly using Data::Dump instead
use strict;
use warnings 'all';
use feature 'say';
use List::Util 'min';
use List::UtilsBy 'min_by';
my %data = (
"0" => {
distance2enemybase => 6755,
distance2mybase => 10598,
id => 0,
snaffle => {
4 => { distance => 2125, id => 4, target => "true", x => 11331, y => 5291 },
5 => { distance => 9734, id => 5, x => 875, y => 3856 },
6 => { distance => 510, id => 6, x => 10517, y => 6741 },
7 => { distance => 8485, id => 7, x => 4591, y => 544 },
8 => { distance => 2236, id => 8, x => 11709, y => 5475 },
},
state => 0,
type => "WIZARD",
x => 10084,
y => 7012,
},
);
my $snaffles = $data{0}{snaffle};
my $min_distance = min map { $snaffles->{$_}{distance} } keys %$snaffles;
# OR
my $min_distance = min map { $_->{distance} } values %$snaffles;
my $closest_snaffle = min_by { $snaffles->{$_}{distance} } keys %$snaffles;
say "\$min_distance = $min_distance";
say "\$closest_snaffle = $closest_snaffle";
output
$min_distance = 510
$closest_snaffle = 6

Data::Dumper::Freezer proper usage

I'm trying to log data structures in an old and big Perl project. In order to do so, I use Data::Dumper, however, some structures are a bit too large and spam the log. So I'm looking for a way to log them in a less verbose manner.
Now Dumper's doc mentions $Data::Dumper::Freezer = <method_name> variable that can be used to fix that. I've tried using that.
Adding a serializer method that returns "shortened" value results in nothing, though the method gets called. Making the serializer method act on $_[0] causes the needed effect, but spoils the original data structure.
I'm confused. What am I doing wrong? How can I fix it?
Here's a refined sample code:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
$Data::Dumper::Indent = 0;
$\="\n";
my $x = Foo->new ( answer => 42, use => "force" );
my $y = { foo => $x };
print "initial plain:\n\t", Dumper( $x );
print "initial compound:\n\t", Dumper( $y );
{
local $Data::Dumper::Freezer = 'freeze_pure';
print "still not abbreviated data:\n\t", Dumper( $y );
};
{
local $Data::Dumper::Freezer = 'freeze_replace';
print "abbreviated data:\n\t", Dumper( $y );
};
print "initial data is still intact:\n\t", Dumper( $x );
print "compound data is corrupted:\n\t", Dumper( $y );
package Foo;
sub new {
my $class = shift;
return bless { #_ }, $class;
};
sub freeze_pure {
my $self = $_[0];
warn "# In freeze_pure";
return bless {
values => join ",", values %$self
}, (ref $self) . "::short";
};
sub freeze_replace {
my $self = $_[0];
warn "# In freeze_replace";
$_[0] = bless {
values => join ",", values %$self
}, (ref $self) . "::short";
return;
};
And output:
initial plain:
$VAR1 = bless( {'use' => 'force','answer' => 42}, 'Foo' );
initial compound:
$VAR1 = {'foo' => bless( {'use' => 'force','answer' => 42}, 'Foo' )};
# In freeze_pure at dumper-freezer.pl line 36.
still not abbreviated data:
$VAR1 = {'foo' => bless( {'use' => 'force','answer' => 42}, 'Foo' )};
# In freeze_replace at dumper-freezer.pl line 42.
abbreviated data:
$VAR1 = {'foo' => bless( {'values' => 'force,42'}, 'Foo::short' )};
initial data is still intact:
$VAR1 = bless( {'use' => 'force','answer' => 42}, 'Foo' );
compound data is corrupted:
$VAR1 = {'foo' => bless( {'values' => 'force,42'}, 'Foo::short' )};
Although the documentation is a bit sparse, the intended use of freezer/toaster is data serialization/de-serialization, not prettification of debugging output.
So, Data::Dumper calls the freezer method, but doesn't use the return value. The idea is probably that if you're going to serialize an object, you won't be messing with it again until you de-serialize it, so there's no problem with changing the object itself.
Here's the relevant section of code from the Data::Dumper source:
# Call the freezer method if it's specified and the object has the
# method. Trap errors and warn() instead of die()ing, like the XS
# implementation.
my $freezer = $s->{freezer};
if ($freezer and UNIVERSAL::can($val, $freezer)) {
eval { $val->$freezer() };
warn "WARNING(Freezer method call failed): $#" if $#;
}
If you just want to reduce the size of the output in your logs, you can remove newlines and indentation by setting $Data::Dumper::Indent to zero:
use Data::Dumper;
use WWW::Mechanize;
$Data::Dumper::Indent = 0;
my $mech = WWW::Mechanize->new;
print Dumper $mech;
Output:
$VAR1 = bless( {'headers' => {},'ssl_opts' => {'verify_hostname' => 1},'forms' => undef,'page_stack' => [],'text' => undef,'requests_redirectable' => ['GET','HEAD','POST'],'timeout' => 180,'onerror' => sub { "DUMMY" },'current_form' => undef,'links' => undef,'max_redirect' => 7,'quiet' => 0,'images' => undef,'noproxy' => 0,'stack_depth' => 8675309,'show_progress' => undef,'protocols_forbidden' => undef,'no_proxy' => [],'handlers' => {'request_prepare' => bless( [{'owner' => 'LWP::UserAgent::cookie_jar','callback' => sub { "DUMMY" },'line' => '/home/foo/perl5/perlbrew/perls/perl-5.16.3/lib/site_perl/5.16.3/LWP/UserAgent.pm:705'}], 'HTTP::Config' ),'response_header' => bless( [{'owner' => 'LWP::UserAgent::parse_head','callback' => sub { "DUMMY" },'m_media_type' => 'html','line' => '/home/foo/perl5/perlbrew/perls/perl-5.16.3/lib/site_perl/5.16.3/LWP/UserAgent.pm:684'}], 'HTTP::Config' ),'response_done' => bless( [{'owner' => 'LWP::UserAgent::cookie_jar','callback' => sub { "DUMMY" },'line' => '/home/foo/perl5/perlbrew/perls/perl-5.16.3/lib/site_perl/5.16.3/LWP/UserAgent.pm:708'}], 'HTTP::Config' )},'onwarn' => sub { "DUMMY" },'protocols_allowed' => undef,'use_eval' => 1,'local_address' => undef,'autocheck' => 1,'title' => undef,'def_headers' => bless( {'user-agent' => 'WWW-Mechanize/1.75'}, 'HTTP::Headers' ),'cookie_jar' => bless( {'COOKIES' => {}}, 'HTTP::Cookies' ),'proxy' => {},'max_size' => undef}, 'WWW::Mechanize' );
This is still a lot of output, but it's certainly more compact than:
$VAR1 = bless( {
'headers' => {},
'ssl_opts' => {
'verify_hostname' => 1
},
'forms' => undef,
'page_stack' => [],
'text' => undef,
'requests_redirectable' => [
'GET',
'HEAD',
'POST'
],
'timeout' => 180,
'onerror' => sub { "DUMMY" },
'current_form' => undef,
'links' => undef,
'max_redirect' => 7,
'quiet' => 0,
'images' => undef,
'noproxy' => 0,
'stack_depth' => 8675309,
'show_progress' => undef,
'protocols_forbidden' => undef,
'no_proxy' => [],
'handlers' => {
'request_prepare' => bless( [
{
'owner' => 'LWP::UserAgent::cookie_jar',
'callback' => sub { "DUMMY" },
'line' => '/home/foo/perl5/perlbrew/perls/perl-5.16.3/lib/site_perl/5.16.3/LWP/UserAgent.pm:705'
}
], 'HTTP::Config' ),
'response_header' => bless( [
{
'owner' => 'LWP::UserAgent::parse_head',
'callback' => sub { "DUMMY" },
'm_media_type' => 'html',
'line' => '/home/foo/perl5/perlbrew/perls/perl-5.16.3/lib/site_perl/5.16.3/LWP/UserAgent.pm:684'
}
], 'HTTP::Config' ),
'response_done' => bless( [
{
'owner' => 'LWP::UserAgent::cookie_jar',
'callback' => sub { "DUMMY" },
'line' => '/home/foo/perl5/perlbrew/perls/perl-5.16.3/lib/site_perl/5.16.3/LWP/UserAgent.pm:708'
}
], 'HTTP::Config' )
},
'onwarn' => sub { "DUMMY" },
'protocols_allowed' => undef,
'use_eval' => 1,
'local_address' => undef,
'autocheck' => 1,
'title' => undef,
'def_headers' => bless( {
'user-agent' => 'WWW-Mechanize/1.75'
}, 'HTTP::Headers' ),
'cookie_jar' => bless( {
'COOKIES' => {}
}, 'HTTP::Cookies' ),
'proxy' => {},
'max_size' => undef
}, 'WWW::Mechanize' );
Alternatively, you could try Data::Dump, which allows you to filter the output using Data::Dump::Filtered. I prefer Data::Dump to Data::Dumper anyway because I think it has more sensible defaults (e.g. outputting escape sequences for whitespace other than spaces).
I haven't used the filtering feature yet, but brian d foy wrote a nice article about it with several examples.

accessing Data::Dumper output

I have this Perl subroutine:
sub ask_for_lease {
my $url = '/sp/api/v1/lease';
my $formdata = '{"classname":"lease",}';
my $c = REST::Client->new();
$c->setHost($wizhost);
$c->PUT (
$url
, $formdata
, $headers
);
my $r = from_json($c->responseContent());
#print Dumper($r);
#my #results = $r->{'results'};
my #items = %{#{$r->{'results'}}[0]}->{'items'};
print Dumper(#items);
for my $item (#items) {
print "=============\n";
print Dumper($item);
print "=============\n";
}
}
It produces this output:
$VAR1 = [
{
'owner' => undef,
'notes' => 'Simulation One',
'version' => undef,
'status' => 'Ready',
'name' => 'One',
'lease' => '7070',
'_oid' => '1'
},
{
'owner' => undef,
'notes' => 'Simulation Two',
'version' => undef,
'status' => 'Ready',
'name' => 'Two',
'lease' => '2',
'_oid' => '2'
},
{
'owner' => undef,
'notes' => 'Simulation Three',
'version' => undef,
'status' => 'Ready',
'name' => 'Three 2012',
'lease' => '3',
'_oid' => '3'
},
...
];
=============
$VAR1 = [
{
'owner' => undef,
'notes' => 'Simulation One',
'version' => undef,
'status' => 'Ready',
'name' => 'One',
'lease' => '7070',
'_oid' => '1'
},
{
'owner' => undef,
'notes' => 'Simulation Two',
'version' => undef,
'status' => 'Ready',
'name' => 'Two',
'lease' => '2',
'_oid' => '2'
},
{
'owner' => undef,
'notes' => 'Simulation Three',
'version' => undef,
'status' => 'Ready',
'name' => 'Three 2012',
'lease' => '3',
'_oid' => '3'
},
...
];
=============
What I want to be able to do is iterate over the items array and print out the status and the name, but I am not sure I am dereferencing $r correctly.
The line
my #items = %{#{$r->{'results'}}[0]}->{'items'}
is very suspicious. You are extracting the first element of the array referred to by $r->{results}, dereferencing that as a hash, and using that hash in reference syntax. You should have got
Using a hash as a reference is deprecated
if you have use strict and use warnings in place as you should.
It is best to extract complex nested data in layers. In this case you can get the reference to the items array into a scalar variable and use that.
my $items= $r->{results}[0]{items};
for my $item ( #$items ) {
printf "name: %s, $item->{name};
printf "status: %s, $item->{status};
print "--\n";
}
If you post your JSON data then we will be able to help much better

Combining 2+ 'deep' (multi-dimensional) hashes in perl

There is a question that explains exactly what I want here: how to merge 2 deep hashes in perl
However, the answer there does not seem to work for me (suggestions of using the Merge module).
I have two hashes like so:
$VAR1 = {
'57494' => {
'name' => 'John Smith',
'age' => '9',
'height' => '120'
},
'57495' => {
'name' => 'Amy Pond',
'age' => '17',
'height' => '168'
}
}
};
$VAR1 = {
'57494' => {
'name_address' => 'Peter Smith',
'address' => '5 Cambridge Road',
'post_code' => 'CR5 0FS'
}
}
};
If I use Hash::Merge or the %c = {%a,%b} format I get this every time:
$VAR1 = '57494';
$VAR2 = {
'name_address' => 'Peter Smith',
'address' => '5 Cambridge Road',
'post_code' => 'CR5 0FS'
};
(so it basically overwrote the first data with the second and messed up the keys) when I want:
$VAR1 = {
'57494' => {
'name' => 'John Smith',
'age' => '9',
'height' => '120'
'name_address' => 'Peter Smith',
'address' => '5 Cambridge Road',
'post_code' => 'CR5 0FS'
},
'57495' => {
'name' => 'Amy Pond',
'age' => '17',
'height' => '168'
}
}
};
So when the keys are the same, the data merges together, otherwise the new keys are just appended onto the end. I hope this make sense. Maybe I've done something incorrectly using Merge or need to 'manually' add them in loops, but I'm spending too much time thinking about it, regardless!
Edit: how I use Merge to see if I'm doing something silly:
I have:
use Hash::Merge qw( merge );
...hash data above as %hash1 and %hash2...
my %combined_hash = %{ merge( %hash1,%hash2 ) };
print Dumper(%combined_hash);
If I do it with references, it works like a charm.
use strict; use warnings;
use Data::Dumper;
use Hash::Merge qw(merge);
my $h1 = {
'57494' => {
'name' => 'John Smith',
'age' => '9',
'height' => '120'
},
'57495' => {
'name' => 'Amy Pond',
'age' => '17',
'height' => '168'
}
};
my $h2 = {
'57494' => {
'name_address' => 'Peter Smith',
'address' => '5 Cambridge Road',
'post_code' => 'CR5 0FS'
}
};
my $h3 = merge( $h1, $h2 );
print Dumper $h3;
Output:
$VAR1 = {
'57495' => {
'name' => 'Amy Pond',
'age' => '17',
'height' => '168'
},
'57494' => {
'name_address' => 'Peter Smith',
'name' => 'John Smith',
'post_code' => 'CR5 0FS',
'address' => '5 Cambridge Road',
'height' => '120',
'age' => '9'
}
};
If, however, I do it with hashes instead of hash refs, it doesn't:
my %hash1 = (
'57494' => {
'name' => 'John Smith',
'age' => '9',
'height' => '120'
},
'57495' => {
'name' => 'Amy Pond',
'age' => '17',
'height' => '168'
}
);
my %hash2 = (
'57494' => {
'name_address' => 'Peter Smith',
'address' => '5 Cambridge Road',
'post_code' => 'CR5 0FS'
}
);
my %h3 = merge( %hash1, %hash2 );
print Dumper \%h3;
__END__
$VAR1 = {
'57495' => undef
};
That is because the merge from Hash::Merge can only take references, but you are passing it hashes. In addition, you need to call it in scalar context.
Try it like so:
# +--------+--- references
# ,-- SCALAR context | |
my $combined_hash = %{ merge( \%hash1, \%hash2 ) };
print Dumper($combined_hash);
for my $key (keys %fromhash) {
if(not exists $tohash{$key}) {
$tohash{$key} = {};
}
for my $subkey (keys %{$fromhash{$key}}) {
${$tohash{$key}}{$subkey} = ${$fromhash{$key}}{$subkey};
}
}
With more or less braces depending on whether my last coffee was any good.
Python is definitely more comfortable for this kind of thing, because it doesn't make you think about references:
for key in fromdict:
if key not in todict:
todict[key] = {}
todict[key] = dict(fromdict[key].items() + todict[key].items())
Or if todict is a defaultdict (creating keys on read as well as assignment):
for key in fromdict:
todict[key] = dict(dict(fromdict[key]).items() + dict(todict[key]).items())