How to fetch key of a hash through values in Hash of Arrays - perl

Below is the small code snippet i have created using Hash Key and Hash Value which is an array.
And my input is Hash value (Array in this case) , i have to search for all the arrays in $ENV hash and fetch the hash key
Example if i search for m3d1 , the output should be TEST3
use strict;
use warnings;
use Data::Dumper;
my %ENV;
$ENV{"TEST3"}=["m3d1","m3d2"] ;
$ENV{"TEST4"}=["m4d1","m4d2"] ;
$ENV{"TEST5"}=["m5d1","m5d2"] ;
$ENV{"TEST6"}=["m6d1","m6d2"] ;
print Dumper \#keys;
print Dumper \#values;
Is it possible , or is there a better design ?

Of course it's possible. We can visit every value in the data structure until we find the one that matches.
You didn't say what the strings represent, to I'm going to call TEST3 a group, and I'm going to call m3d1 a host. The following snippets don't assume a host is preset in only one of group.
my #groups;
for my $group (keys(%ENV)) {
for my $host (#{ $ENV{$group} }) {
if ($host eq $target_host) {
push #groups, $group;
}
}
}
die("Not found\n") if !#groups;
say for #groups;
But this isn't efficient. If you were planning on doing many lookups, this would be slow.
Let's start by turning the structure inside out.
my %groups_by_host;
for my $group (keys(%ENV)) {
for my $host (#{ $ENV{$group} }) {
push #{ $groups_by_host{$host} }, $group;
}
}
The above produces
my %groups_by_host = (
m3d1 => [ "TEST3" ],
m3d2 => [ "TEST3" ],
m4d1 => [ "TEST4" ],
m4d2 => [ "TEST4" ],
m5d1 => [ "TEST5" ],
m5d2 => [ "TEST5" ],
m6d1 => [ "TEST6" ],
m6d2 => [ "TEST6" ],
);
Then, searching becomes instantaneous.
my $groups = $groups_by_host{$target_host}
or die("Not found\n");
say for #$groups;

Related

Search whether AoH value exists in same Hash

I have hash which contains some data.
I want my final %hash to be printed like this:
'UGroup=1' => [ 'C72', 'C73', 'C71' ]
Here is my script:
use Data::Dumper;
my %h = (
'C72' => [ 'S=2-1' ],
'C73' => [ 'S=3-1' ],
'C71' => [ 'S=91-1'],
'UGroup=1' => [ 'S=1-1',
'S=2-1',
'S=3-1',
'S=91-1'],
);
print Dumper(\%h);
foreach my $C (sort keys %h) {
next unless $C =~ /UGroup/;
for my $f (#{$h{$C}}){
print "\tf:$f\n";
#This is not correct, but wanted to do something like this.
push #{$hash{$C}}, $f if(exists $h{$f});
}
}
print Dumper(\%hash);
Here in example input hash I need to check if S=91-1 has any key? If yes then associate that key to value for %hash with its original key.
How can I do that?
You didn't name the things, so
S=91-1 shall be a snake,
C71 shall be a cow, and
UGroup=1 shall be a group.
Start by building this hash:
my %cows_by_snake = (
'S=91-1' => [ 'C71' ],
'S=2-1' => [ 'C72' ],
'S=3-1' => [ 'C73' ],
);
Just ignore the keys that of %h that are groups when you do so.
Once you built a hash, it's simply a question of doing the following:
Create an empty result hash.
For each group,
Create an empty collection of cows.
For each snake associated the the group,
Add the cows associated with the snake to the collection.
Eliminate the duplicates in the collection of cows.
Add the group and the associated cows to the result hash.
my #groups;
my #cows;
for my $cow_or_group (keys(%h)) {
if ($cow_or_group =~ /^UGroup=/) {
push #groups, $cow_or_group;
} else {
push #cows, $cow_or_group;
}
}
my %cows_by_snake;
for my $cow (#cows) {
for my $snake (#{ $h{$cow} }) {
push #{ $cows_by_snake{$snake} }, $cow;
}
}
my %results;
for my $group (#groups) {
my %group_cows;
for my $snake (#{ $h{$group} }) {
for my $cow (#{ $cows_by_snake{$snake} }) {
++$group_cows{$cow};
}
}
$results{$group} = [ sort keys %group_cows ];
}

Hash content extraction based on condition

I have a hash containing node data.
I am expecting hash content to be printed in -r_<count> and -d_<count> attributes.
Here is the script:
use strict; use warnings;
use Data::Dumper;
my %hash = (
'Network=Test,Cell=31' => [ 'Network=Test,Unit=RU-1-1,Port=A',
'Network=Test,Unit=RU-1-2,Port=A'
],
'Network=Test,Cell=32' => [ 'Network=Test,Unit=RU-1-1,Port=A',
'Network=Test,Unit=RU-1-2,Port=A'
],
'Network=Test,Cell=33' => [ 'Network=Test,Unit=RU-1-5,Port=A',
'Network=Test,Unit=RU-1-6,Port=A'
],
);
print "hash:\n".Dumper(\%hash);
my $count = 0;
foreach my $d (sort keys %hash) {
$count++;
print "-d_". $count."=".$d . "\n";
my %seen = ();
foreach my $r (sort #{$hash{$d}}) {
$seen{$r}++;
}
if ((keys %seen) > 0) {
my $uniq = join ("###",sort keys %seen);
print "-r_". $count . "=" . $uniq . "\n";
} else {
print "-r_". $count."="."NA\n";
}
}
And I am able to print output like below(current output):
-d_1=Network=Test,Cell=31
-r_1=Network=Test,Unit=RU-1-1,Port=A###Network=Test,Unit=RU-1-2,Port=A
-d_2=Network=Test,Cell=32
-r_2=Network=Test,Unit=RU-1-1,Port=A###Network=Test,Unit=RU-1-2,Port=A
-d_3=Network=Test,Cell=33
-r_3=Network=Test,Unit=RU-1-5,Port=A###Network=Test,Unit=RU-1-6,Port=A
But I want output to be printed like below (expected output):
-r_1=Network=Test,Unit=RU-1-1,Port=A
-d_1=Network=Test,Cell=31###Network=Test,Cell=32
-r_2=Network=Test,Unit=RU-1-2,Port=A
-d_2=Network=Test,Cell=31###Network=Test,Cell=32
-r_3=Network=Test,Unit=RU-1-5,Port=A
-d_3=Network=Test,Cell=33
-r_4=Network=Test,Unit=RU-1-6,Port=A
-d_4=Network=Test,Cell=33
The expected output is, the value of -r_<count> should be printed as singular (from %hash keys array value) and -d_<count> (from %hash keys) should printed.
The output is guided by the unique values of the arrays. Your output loop must therefore iterate over these.
(
'Network=Test,Unit=RU-1-1,Port=A',
'Network=Test,Unit=RU-1-2,Port=A',
'Network=Test,Unit=RU-1-5,Port=A',
'Network=Test,Unit=RU-1-6,Port=A',
)
However, for each of these, the output needs the associated keys. This means the output loop requires the following data:
(
'Network=Test,Unit=RU-1-1,Port=A' => [ 'Network=Test,Cell=31', 'Network=Test,Cell=32' ],
'Network=Test,Unit=RU-1-2,Port=A' => [ 'Network=Test,Cell=31', 'Network=Test,Cell=32' ],
'Network=Test,Unit=RU-1-5,Port=A' => [ 'Network=Test,Cell=33' ],
'Network=Test,Unit=RU-1-6,Port=A' => [ 'Network=Test,Cell=33' ],
)
Basically, your data structure is inside-out. But now that we know what we want, it's just a question of transforming the data structure into what we need.
my %foos_by_bar;
for my $foo (keys %hash) { # %hash_b
my $bars = $hash{$foo}; # %hash_a
for my $bar (#$bars) {
push #{ $foos_by_bar{$bar} }, $foo;
}
}
The output loop simply needs to iterate over the (possibly sorted) keys of %foos_by_bar, and #{ $foos_by_bar{$bar} } contains the data you need for -d.
Nothing's stopping you from iterating over the sorted keys of %foos_by_bar in the output loop to produce predictable output, but that won't necessarily give you the same order as in the question. If you need that specific order, you can use the following:
my #bars;
my %foos_by_bar;
for my $foo (sort keys %hash) { # %hash_b
my $bars = $hash{$foo}; # %hash_a
for my $bar (#$bars) {
push #bars, $bar if !$foos_by_bar{$bar};
push #{ $foos_by_bar{$bar} }, $foo;
}
}
In this case, the output loop would iterate over #bars.

Perl: How to access a hash with array values in a loop?

I've defined a hash with arrays as values:
%myhash = ( 'key1' => [ 'key1value1', 'key1value2' ],
'key2' => [ 'key2value1', 'key2value2' ],
.......
'key100' => [ 'key100value1', 'key100value2' ] );
How can we access the values of each key using a loop?
Basically, I want to do something like:
print "$myhash{'key1'}->[0] and $myhash{'key1'}->[1]\n";
print "$myhash{'key2'}->[0] and $myhash{'key2'}->[1]\n";
.......
print "$myhash{'key100'}->[0] and $myhash{'key100'}->[1]\n";
to print those two array values of each key in separate lines.
I've tried using an index in for/while loops with no success.
Thanks for any suggestions in advance!
Here my solution...just for fun and maybe even more concise. Not sure if you can compress this even further:
print join ' ', #$_, "\n" for #myhash{sort keys %myhash};
Works for arbitrary long value arrays in %myhash.
EDIT:
As simbabque pointed out in the comments, it can indeed be further compressed. Thanks for the hint!
say join ' ', #$_ for #myhash{sort keys %myhash}; # requires: use feature 'say';
Here's one way:
%myhash = ( 'key1' => [ 'key1value1', 'key1value2' ],
'key2' => [ 'key2value1', 'key2value2' ],
'key3' => [ 'key3value1', 'key3value2' ] );
for my $k (sort keys %myhash)
{
print "$myhash{$k}->[0] $myhash{$k}->[1]\n";
}
If the number of array elements varies:
for my $k (sort keys %myhash)
{
for my $v (#{$myhash{$k}})
{
print "$v ";
}
print "\n";
}
Another solution :
print map { join(" ", #{ $myhash{$_} }, "\n") } sort keys %myhash;
(the concise way)

Printing only parts of a hash

So I have a hash in perl setup that has multiple values for each field, i used this to push data into the hash:
push #{$user{$infoName}}, $information;.
eg. a user my have 3 favourite shows all stored as
{'favourite_TV_shows' => [ 'A Country Practice', 'All Saints', 'Falling Skies' ], 'weight' => [ '53kg' ]}
Some of this user info is private such as weight so the fields i want to show are stored in the array #fieldsToPrint = ['username','favourite_TV_shows']
how can i write a foreach loop to print only the fields that are in the feildsToPrint array.
The following is my attempt so far...
foreach ($user{$infoName} == #fieldsToPrint){
#print
}
Just iterate on your #fieldsToPrint array, skipping those keys that don't have a value:
use strict;
use warnings;
my #fieldsToPrint = ( 'username', 'favourite_TV_shows' );
my %user = (
'favourite_TV_shows' => [ 'A Country Practice', 'All Saints', 'Falling Skies' ],
'weight' => ['53kg'],
);
for my $key (#fieldsToPrint) {
next if !$user{$key};
print "$key = ", join(', ', #{ $user{$key} }), "\n";
}
Outputs:
favourite_TV_shows = A Country Practice, All Saints, Falling Skies

Anomalous push behaviour under Catalyst MVC

I would expect the following code
my #array;
for my $rapport ( qw( value1 value2 value3 ) ) {
push #array, { key => $rapport };
}
to produce:
$VAR1 = [
{
'key' => 'value1'
},
{
'key' => 'value2'
},
{
'key' => 'value3'
}
];
However, running this code segment under Catalyst MVC I get:
$VAR1 = [
{
'key' => [ 'value', 'value2', 'value3' ]
},
];
Can someone please explain to me why?
EDIT: could anyone with the same issue please add an example? I cannot reproduce after some code changes, but as it has been upvoted 5 times I assume some other users have also experienced this issue?
This code example...
#!/usr/bin/perl
use Data::Dumper;
my #input = ( "var1", "var2", "var3" );
my #array;
for my $rapport ( #input ) {
push #array, { key => $rapport };
}
print Dumper( \#array );
exit;
produces ...
$VAR1 = [
{
'key' => 'var1'
},
{
'key' => 'var2'
},
{
'key' => 'var3'
}
];
But the following...
#!/usr/bin/perl
use Data::Dumper;
my #input = [ "var1", "var2", "var3" ]; # sometimes people forget to dereference their variables
my #array;
for my $rapport ( #input ) {
push #array, { key => $rapport };
}
print Dumper( \#array );
exit;
shows...
$VAR1 = [
{
'key' => [
'var1',
'var2',
'var3'
]
}
];
As you can see both examples loop through an array but the second one is an array, that was initialized with a reference value. Since in Catalyst you normally ship various values through your application via stash or similar constructs, you could check weather your array really contains scalar values : )