Declare hash variable in loop - perl

I need to use a hash and loop in my code. Please see the sample code it's not working. i wanted to print the variable wafer, site and res side by side so it will look like this
1, 1, 63
1, 2, -53
1, 3, 9.47
1, 4, 9.55
1, 5, -8.32
my #wafer = ("1","1","1","1","1");
my #site = ("1", "2", "3", "4", "5");
my #res = ("63","-53","9.47","9.55","-8.32");
my %hash;
foreach my $result(#res) {
$hash{$wafer[0]}{$site[0]} = $result;
last;
}
print "$wafer{$wafer[0]}{$site[0]} \n";

When you want to iterate several arrays synchronously, iterate over the indices:
for my $index (0 .. $#wafer) {
print "$wafer[$index] $site[$index] $res[$index]\n";
}
You also might want to build a hash keyed by the site (as it's the only unique value):
for my $index (0 .. $#wafer) {
$hash{ $site[$index] } = { wafer => $wafer[$index],
res => $res[$index] };
}
This will create a hash like this:
%hash = (
'4' => {
'res' => '9.55',
'wafer' => '1'
},
'3' => {
'wafer' => '1',
'res' => '9.47'
},
'1' => {
'res' => '63',
'wafer' => '1'
},
'2' => {
'res' => '-53',
'wafer' => '1'
},
'5' => {
'res' => '-8.32',
'wafer' => '1'
}
);

Related

how to create reference to a value of a subhash member in Perl?

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

Can't use string as a HASH reference

Here's the structure that I'm trying to access
Dumper $resourceAudit
$VAR1 = '{
\'rh6\' => {
\'h\' => 1,
\'n\' => 1
},
\'win2k8\' => {
\'h\' => 1,
\'n\' => 1
},
\'win2k12\' => {
\'h\' => 3,
\'n\' => 3
},
\'win2k3\' => {
\'h\' => 0,
\'n\' => 1
},
\'usim\' => {
\'h\' => 4,
\'n\' => 4
}
}';
So, I know that $resourceAudit is actually a string and so, %$resourceAudit is sure to give me the Can't use string as a HASH reference error.
Is there any way I can get around this and access the 'rh6' key?
$resourceAudit doesn't contain a reference to a hash; it contains a string. That string is Perl code that would return a reference to a hash when executed. You can use eval EXPR to run Perl code.
my $data = eval($serialized_data)
or die("Error executing audit code: $#");
... %$data ...

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

How to dig into an certain hash depth?

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 } },
}

perl populating hash of hashes recursively

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' => {}
}
};