I have a hash like this:
{ ABC => [1, 2],
1 => [11, 12,13,14],
13 => [17,20] }
I want to generate a hash of hashes like this:
(ABC => { 1 => {11 => {},
12 => {},
13 => { 17 => {}
20 = {} },
14 => {}
},
2 => {}
}
)
The above hash is nothing but a tree with a root node and further child nodes.
I understand we have to use recursion to check child nodes for every parent node. I have looked at the question previously asked here. I am unable to understand how during recursion specific node's data is stored under its particular parent key. In other words how can hash of hashes be populated recursively ?
Appreciate any pointers or explanation.
Thanks for your time
The real problem is that you don't really know what you want.
{ABC => 1 => 11 => {}
=> 1 => 12 => {}
=> 1 => 13 => 17 => {}
=> 20 = {}
=> 1 => 14 => {}
}
is just a really weird way of writing
{
ABC => "1",
11 => {},
1 => "12",
{} => "1",
13 => "17",
{} => "20",
{} => "1",
14 => {},
}
That makes no sense. I think you actually want
{
ABC => {
1 => {
11 => {},
12 => {},
13 => {
17 => {},
20 => {},
},
14 => {},
},
},
}
Now that you know what you want, you should take a stab at implementing it.
You can use the code I defined here: How can I merge several hashes into one hash in Perl?
having defined #hash_list so:
my #hash_list
= { map { ref() eq 'ARRAY' ? { map {; $_ => {} } #$_ } : $_ }
%{{ ABC => [1, 2]
, 1 => [11, 12,13,14]
, 13 => [17,20]
}}
};
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my %data = (
ABC => [1, 2],
1 => [11, 12, 13, 14],
13 => [17, 20]
);
my %hash;
sub modify_hash {
my ($base, $ref) = #_;
for my $k (keys %$ref) {
if (exists $base->{$k}) {
$ref->{$k} = $base->{$k};
delete $base->{$k};
}
modify_hash($base, $ref->{$k});
}
}
map { %{$hash{$_}} = map { $_ => {}; } #{$data{$_}}; } keys %data;
map { modify_hash(\%hash, $hash{$_}); } keys %hash;
print Dumper(\%hash);
output:
$VAR1 = {
'ABC' => {
'1' => {
'11' => {},
'13' => {
'17' => {},
'20' => {}
},
'12' => {},
'14' => {}
},
'2' => {}
}
};
Related
I would like to use reference to make the code shorter
I make it simple, only one level of depth here :
my %cx = ( 'a' => ( "A" => 7, "B" => 8), 'b' => ( "Z" => 20 ));
# I want a ref to the B's value for testing, with a possible increment action :
my $ref = \$cx{a}{B}; # so I just put a \ before
if ($$ref and $$ref < 10) { $$ref ++; } # will give $cx{a}{B} = 9
# I have the same need for inner references, for example :
my $ref = \$stock{$stockName}->{places}->{$otherHashRef->{andItsKey}}
But this doesn't work
Parens don't contruct anything; they just change precedence. So,
my %cx = ( 'a' => ( "A" => 7, "B" => 8), 'b' => ( "Z" => 20 ));
is just a weird way of writing
my %cx = ( 'a' => "A", 7 => "B", 8 => 'b', "Z" => 20 );
Curlies create a hash and returns a reference to a hash, so you want
my %cx = ( 'a' => { "A" => 7, "B" => 8 }, 'b' => { "Z" => 20 });
For example,
$ perl -MData::Dumper -e'
{
my %cx = ( 'a' => { "A" => 7, "B" => 8 }, 'b' => { "Z" => 20 });
my $ref = \$cx{a}{B};
++$$ref;
print(Dumper(\%cx));
}
{
my %stock;
my $stockName = "abc";
my $otherHashRef = { andItsKey => 'def' };
my $ref = \$stock{$stockName}->{places}->{$otherHashRef->{andItsKey}};
++$$ref;
print(Dumper(\%stock));
}
'
$VAR1 = {
'a' => {
'A' => 7,
'B' => 9
},
'b' => {
'Z' => 20
}
};
$VAR1 = {
'abc' => {
'places' => {
'def' => 1
}
}
};
For example, this is my hash
'Level1_A' => {
'Level2_A' => 1071,
'Level2_B' => 3429,
'Level2_C' => 3297
},
'Level1_B' => {
'Level2_A' => 702,
'Level2_B' => 726
},
And I want an output which should look like
'Level1_A' => {
'Level2_B' => 3429,
'Level2_C' => 3297,
'Level2_A' => 1071
},
'Level1_B' => {
'Level2_B' => 726,
'Level2_A' => 702
},
Ultimately, I want to the keys corresponding to the highest values.
Level1_A___Level2_B___3429
Level1_B___Level2_A____726
Hashes are inherently unordered and there is no way to sort them. However, you can find the maximum of the values of a hash and it is best to use a module to help
List::UtilsBy provides max_by and will allow you to find the hash key corresponding to the maximum numeric value
Like this
use strict;
use warnings 'all';
use List::UtilsBy 'max_by';
my $data = {
'Level1_A' => {
'Level2_A' => 1071,
'Level2_B' => 3429,
'Level2_C' => 3297,
},
'Level1_B' => {
'Level2_A' => 702,
'Level2_B' => 726
},
};
for my $k1 ( sort keys %$data ) {
my $v1 = $data->{$k1};
my $k2 = max_by { $v1->{$_} } keys %$v1;
printf "%s %s %s\n", $k1, $k2, $v1->{$k2};
}
output
Level1_A Level2_B 3429
Level1_B Level2_B 726
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
I have a hash where I don't know its depth. I got it with DBI::selectall_hashref where the second parameter is given by the user.
So depending on the query I can have something like this for a 2-levels hash.
hash_ref = (
aphrodite => (
foo => (
name => aphrodite,
foobar => foo
a => 1,
b => 2,
)
bar => (
name => aphrodite,
foobar => bar
a => 1,
b => 2,
)
)
apollo => (
...
)
ares => (
...
)
)
As you can see the key columns are redundant into the hash. I would like to remove the redundant keys.
If I know that this is a 2-levels hash I can easily solve my problem with this:
for my $name (keys $hash_ref) {
for my $foobar (keys $hash_ref->{$name}) {
my $h = $hash_ref->{$name}{$foobar};
delete $h->{name};
delete $h->{foobar};
}
}
However with a 3-levels hash I will need 3 cascaded for-loop and so on.
How can I dynamically remove the redundant keys from $hash_ref i.e. name and foobar?
My initial idea was to recursively iterate trough my hash:
iterate($hash_ref, scalar #keys);
sub iterate {
my ($ref, $depth) = #_;
for(keys $ref) {
if ($depth > 0) {
iterate($ref->{$_}, $depth - 1);
}
else {
delete $ref->{$_} for(#keys);
}
}
}
It works but It's ugly, very ugly... Before going any further I would like to know if I missed something. Perhaps the solution could be much simpler that I think.
Any ideas?
More details?
I am writing a database fetcher that takes a user configuration that contains the SQL query $sql and the hash keys #keys. So I get the values from the database with:
$dbh->selecthall_hashref($sql, \#keys, {}, #bind);
I also have to clean fetched data according to additional. Do apply these rules, I have to iterate into the deepest level of $hash_ref to access the keys/values.
I think this does what you need. Essentially it recurses through the hash until it finds a layer where the hash values aren't references. Then it removes the elements from that layer with the keys in #keys
use strict;
use warnings;
use 5.010;
use Data::Dump;
use List::Util 'any';
my $hash_ref = {
aphrodite => {
bar => { name => "aphrodite", foobar => "bar", a => 3, b => 4, },
foo => { name => "aphrodite", foobar => "foo", a => 1, b => 2, },
},
apollo => {
bar => { name => "apollo", foobar => "bar", a => 7, b => 8, },
foo => { name => "apollo", foobar => "foo", a => 5, b => 6, },
},
ares => {
bar => { name => "ares", foobar => "bar", a => 11, b => 12, },
foo => { name => "ares", foobar => "foo", a => 9, b => 10, },
},
};
my #keys = qw/ name foobar /;
remove_dups($hash_ref, \#keys);
dd $hash_ref;
sub remove_dups {
my ($href, $keys) = #_;
if ( any { ref } values %$href ) {
remove_dups($_, $keys) for values %$href;
}
else {
delete #{$href}{#$keys};
}
}
output
{
aphrodite => { bar => { a => 3, b => 4 }, foo => { a => 1, b => 2 } },
apollo => { bar => { a => 7, b => 8 }, foo => { a => 5, b => 6 } },
ares => { bar => { a => 11, b => 12 }, foo => { a => 9, b => 10 } },
}
Out of curiosity, is there another way to extract a subset of my AoH structure? The AoH is 'rectangular' (i.e. guaranteed to have the same keys across all hashrefs).
The use of a temp var and nested maps seems a bit too much for what is essentially a fancy hash slice:
use strict;
use warnings;
use Data::Dump 'dump';
my $AoH = [ # There are many more keys in the real structure
{ a => "0.08", b => "0.10", c => "0.25" },
{ a => "0.67", b => "0.85", c => "0.47" },
{ a => "0.06", b => "0.57", c => "0.84" },
{ a => "0.15", b => "0.67", c => "0.90" },
{ a => "1.00", b => "0.36", c => "0.85" },
{ a => "0.61", b => "0.19", c => "0.70" },
{ a => "0.50", b => "0.27", c => "0.33" },
{ a => "0.06", b => "0.69", c => "0.12" },
{ a => "0.83", b => "0.27", c => "0.15" },
{ a => "0.74", b => "0.25", c => "0.36" },
];
# I just want the 'a's and 'b's
my #wantedKeys = qw/ a b /; # Could have multiple unwanted keys in reality
my $a_b_only = [
map { my $row = $_;
+{
map { $_ => $row->{$_} } #wantedKeys
}
}
#$AoH
];
dump $a_b_only; # No 'c's here
This does it with one map and an arbitrary list of keys:
my #wantedKeys = qw/a b/;
my $wanted = [
map { my %h; #h{#wantedKeys} = #{ $_ }{#wantedKeys}; \%h } #$AoH
];
(With a little help from this post)
If you do not need $AoH anymore, you can use the destructive way:
delete $_->{c} for #$AoH;
You want delete.
my $foo = [ map { delete $_->{c}; $_ } #$AoH ];
If you want to preserve the original data, then you would need to dereference the hashes first.
my $foo = [ map { my %hash = %$_; delete $hash{c}; \%hash; } #$AoH ];
This is my solution (let me introduce the nice Data::Printer module):
use Modern::Perl;
use Data::Printer { colored => 1 };
my $AoH = [
{ a => "0.08", b => "0.10", c => "0.25" },
{ a => "0.67", b => "0.85", c => "0.47" },
{ a => "0.06", b => "0.57", c => "0.84" },
{ a => "0.15", b => "0.67", c => "0.90" },
{ a => "1.00", b => "0.36", c => "0.85" },
{ a => "0.61", b => "0.19", c => "0.70" },
{ a => "0.50", b => "0.27", c => "0.33" },
{ a => "0.06", b => "0.69", c => "0.12" },
{ a => "0.83", b => "0.27", c => "0.15" },
{ a => "0.74", b => "0.25", c => "0.36" },
];
# I just want the 'a's and 'b's, so I build a new hash with the keys I want
my #ab = map { {a=>$_->{a}, b=>$_->{b}} } #$AoH;
p #ab;
# If you don't mind remove the "c" key in your original structure:
# map { delete $_->{c} } #$AoH;
# and $AoH is an array of hashes without the "c" key.