Join from a deep hashref in perl - perl

I have a function called scrape_html what returns an array of deep hashrefs.
The next code prints the correct result:
use 5.014;
use warnings;
for my $table (scrape_html()) {
say join("\t",
$table->{tr}->[0]->{td}->[1],
$table->{tr}->[2]->{td}->[1],
$table->{tr}->[4]->{td}->[1],
);
}
it prints, for example:
r0c1 r2c1 r4c1
I want make it shorter with cleaner row numbers, like the next:
use 5.014;
use warnings;
for my $table (scrape_html()) {
say $table->{tr}->[$_]->{td}->[1] for (qw(0 2 4) );
}
This prints:
r0c1
r2c1
r4c1
The problem is - how to join it? The next code
use 5.014;
use warnings;
for my $table (scrape_html()) {
say join("\t",
(
$table->{tr}->[$_]->{td}->[1] for (qw(0 2 4) )
)
);
}
says:
syntax error at soex.pl line 7, near "] for "
Execution of soex.pl aborted due to compilation errors.
What is the correct syntax?
If someone want a demo of scrape_html
sub scrape_html {
return (
{
'tr' => [
{
'td' => [
'r0c0',
'r0c1'
]
},
{
'td' => [
'r1c0',
'r1c1',
]
},
{
'td' => [
'r2c0',
'r2c1'
]
},
{
'td' => [
'r3c0',
'r3c1'
]
},
{
'td' => [
'r4c0',
'r4c1'
]
},
{
'td' => [
'r5c0',
'r5c1'
]
},
]
}
);
}

You want to use map which returns transformed elements,
say join("\t",
(
map $table->{tr}->[$_]->{td}->[1], qw(0 2 4)
)
);

Related

Get key details from the value through grep

I am trying to find the key name as output by matching $country_value variable in grep through the hash I have.
#!/usr/bin/perl -w
use strict;
use warnings;
my $country_value = 1;
my $country = {
'IN' => [
1,
5
],
'US' => [
2,
6
],
'UK' => [
3,
7
]
};
my $country_details = grep { $_ eq $country_value } values %{$country};
print $country_details;
print "\n";
As per the hash, I need to get the output as IN because the value of IN is 1 and the $country_value is 1, which is what I am trying to find out.
But, I get the output as 0 instead of IN.
Can someone please help?
In your code, values returns a reference to an array. You need to dereference that to get a list for grep.
use warnings;
use strict;
my $country_value = 1;
my $country = {
'IN' => [
1,
5
],
'US' => [
2,
6
],
'UK' => [
3,
7
]
};
my $country_details;
for my $name (keys %{$country}) {
if (grep { $_ == $country_value } #{ $country->{$name} }) {
$country_details = $name;
last;
}
}
print $country_details, "\n";
Prints:
IN

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

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;

executing a function within an array within a hash in perl

I have a Perl data structurte like so
%myhash = (
k1 => v1,
kArray => [
{
name => "anonymous hash",
...
},
\&funcThatReturnsHash,
{
name => "another anonymous hash",
...
}
]
);
Elsewhere I iterate through the list in kArray which contains a bunch of hashes. I would like to either process the actual hash OR the hash returned by the function.
foreach my $elem( #{myhash{kArray}} ) {
if (ref($elem) == "CODE") {
%thisHash = &$elem;
}
else {
%thisHash = %$elem;
}
...
}
However ref ($elem) is always scalar or undefined. I tried func, &func, \&func, \%{&func}, in %myhash to no effect.
how do I extract the hash within the function in the main body?
Apart from the code sample you give being invalid Perl, the main problems seem to be that you are using == to compare strings instead of eq, and you are assigning a hash reference to a hash variable %thishash. I assure you that ref $elem never returns SCALAR with the data you show
It would help you enormously if you followed the common advice to use strict and use warnings at the top of your code
This will work for you
for my $elem ( #{ $myhash{kArray} } ) {
my $this_hash;
if ( ref $elem eq 'CODE' ) {
$this_hash = $elem->();
}
else {
$this_hash = $elem;
}
# Do stuff with $this_hash
}
or you could just use a map like this
use strict;
use warnings;
use 5.010;
use Data::Dump;
my %myhash = (
k1 => v1,
kArray => [
{
name => "anonymous hash",
},
\&funcThatReturnsHash,
{
name => "another anonymous hash",
}
]
);
for my $hash ( map { ref eq 'CODE' ? $_->() : $_ } #{ $myhash{kArray} } ) {
say $hash->{name};
}
sub funcThatReturnsHash {
{ name => 'a third anonymous hash' };
}
output
anonymous hash
a third anonymous hash
another anonymous hash
If you turn on strict and warnings, you'll see that:
foreach my $elem(#{mynahs{kArray}}) {
Isn't valid. You need at the very least a $ before mynahs.
But given something like this - your approach works - here's an example using map to 'run' the code references:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
sub gimme_hash {
return { 'fish' => 'paste' };
}
my $stuff =
[ { 'anon1' => 'value' },
\&gimme_hash,
{ 'anon2' => 'anothervalue' }, ];
my $newstuff = [ map { ref $_ eq "CODE" ? $_->() : $_ } #$stuff ];
print Dumper $newstuff;
Turns that hash into:
$VAR1 = [
{
'anon1' => 'value'
},
{
'fish' => 'paste'
},
{
'anon2' => 'anothervalue'
}
];
But your approach does work:
foreach my $element ( #$stuff ) {
my %myhash;
if ( ref $element eq "CODE" ) {
%myhash = %{$element -> ()};
}
else {
%myhash = %$element;
}
print Dumper \%myhash;
}
Gives:
$VAR1 = {
'anon1' => 'value'
};
$VAR1 = {
'fish' => 'paste'
};
$VAR1 = {
'anon2' => 'anothervalue'
};

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 : )

In Perl, how can I skip an empty key when traversing a hash?

This is my problem, I'm not very knowledgeable in Perl, and I have this function that needs to be fixed.
When this function deviceModelMenu() is called, the CLI displays the following text:
The following models are available
==================================================
1.
2. Cisco1240
3. Catalyst3750
4. Catalyst3650
5. HP2524
The first item is empty, which is wrong, and I need to fix that, the piece of code that displays this menu is:
my $features = shift;
print "=" x 50, "\n";
print "The following models are available\n";
print "=" x 50, "\n";
my $i=1;
foreach (keys %{$features->{features}[0]->{deviceModel}})
{
print "$i. $_ \n";
$i++;
}
If I add the following line:
warn Dumper($features->{features}[0]->{deviceModel});
It dumps this:
$VAR1 = {
'deviceModel' => {
'' => {
'cfg' => []
},
'Cisco1240' => {
'cfg' => [
'cisco1240feature.cfg'
]
},
'Catalyst3750' => {
'cfg' => [
'catalyst3750feature.cfg'
]
},
'Catalyst3650' => {
'cfg' => [
'catalyst3650feature.cfg'
]
},
'HP2524' => {
'cfg' => [
'hp2524feature.cfg'
]
}
}
};
As you may notice, the first item is indeed empty. I added the following line to skip it, and just print the rest of the info:
if ($_ eq '') {
shift;
}
But it doesn't seem to work do what I want. I want to skip the item if it's empty.
Well, shifting #ARGV (implicit argument to shift in main program) nor shifting #_ (implicit argument of shift in a function) are not going to help you, because you are not printing either of them.
You can either:
Not add the '' entry in the first place (depends on how it's generated)
Remove the '' entry before printing:
delete $features->{features}[0]->{deviceModel}->{''};
Don't print the entry:
if($_ eq '') {
next;
}
or
if($_ ne '') {
print "$i. $_ \n";
$i++;
}
foreach (keys %{$features->{features}[0]->{deviceModel}})
{
next unless length($_);
print "$i. $_ \n";
$i++;
}
#!/usr/bin/env perl
use strict; use warnings;
my $devices = {
'deviceModel' => {
'' => { 'cfg' => [] },
'Cisco1240' => { 'cfg' => ['cisco1240feature.cfg' ] },
'Catalyst3750' => { 'cfg' => [ 'catalyst3750feature.cfg' ]},
'Catalyst3650' => { 'cfg' => [ 'catalyst3650feature.cfg' ]},
'HP2524' => { 'cfg' => [ 'hp2524feature.cfg' ]},
}
};
{
my $item = 1;
for my $d (grep length, keys %{ $devices->{deviceModel} }) {
printf "%2d. %s\n", $item++, $d;
}
}
Output:
1. Catalyst3750
2. Cisco1240
3. Catalyst3650
4. HP2524