Perl accessing the elements in a hash/hash reference data structure - perl

I have a question I'm hoping you could help with as I am new to hashes and hash reference stuff?
I have the following data structure:
$VAR1 = {
'http://www.superuser.com/' => {
'difference' => {
'http://www.superuser.com/questions' => '10735',
'http://www.superuser.com/faq' => '13095'
},
'equal' => {
'http://www.superuser.com/ ' => '20892'
}
},
'http://www.stackoverflow.com/' => {
'difference' => {
'http://www.stackoverflow.com/faq' => '13015',
'http://www.stackoverflow.com/questions' => '10506'
},
'equal' => {
'http://www.stackoverflow.com/ ' => '33362'
}
}
If I want to access all the URLs in the key 'difference' so I can then perform some other actions on the URLs, what is the correct or preferred method of accessing those elements?
e.g I will end up with the following URLs that I can then do stuff to in a foreach loop with:
http://www.superuser.com/questions
http://www.superuser.com/faq
http://www.stackoverflow.com/faq
http://www.stackoverflow.com/questions
------EDIT------
Code to access the elements further down the data structure shown above:
my #urls;
foreach my $key1 ( keys( %{$VAR1} ) ) {
print( "$key1\n" );
foreach my $key2 ( keys( %{$VAR1->{$key1}} ) ) {
print( "\t$key2\n" );
foreach my $key3 ( keys( %{$VAR1->{$key1}{$key2}} ) ) {
print( "\t\t$key3\n" );
push #urls, keys %{$VAR1->{$key1}{$key2}{$key3}};
}
}
}
print "#urls\n";
Using the code above why do I get the following error?
Can't use string ("13238") as a HASH ref while "strict refs" in use at ....

It is not difficult, just take the second level of keys off every key in the variable:
my #urls;
for my $key (keys %$VAR1) {
push #urls, keys %{$VAR1->{$key}{'difference'}};
}
If you're struggling with dereferencing, just keep in mind that all values in a hash or array can only be a scalar value. In a multilevel hash or array the levels are just single hashes/arrays stacked on top of each other.
For example, you could do:
for my $value (values %$VAR1) {
push #urls, keys %{$value->{'difference'}};
}
Or
for my $name (keys %$VAR1) {
my $site = $VAR1->{$name};
push #urls, keys %{$site->{'difference'}};
}
..taking the route either directly over the value (a reference to a hash) or over a temporary variable, representing the value via the key. There is more to read in perldoc perldata.

Related

I can't print keys and values in Perl

I have a data structure that I got from this code.
my $name = $data->{Instances}->[0]->{Tags};
That data structure looks like this
$VAR1 = [
{
'Key' => 'Name',
'Value' => 'fl-demo'
},
{
'Value' => 'FL',
'Key' => 'state'
}
];
I'm trying to print the keys and values with this
foreach my $key (sort keys %$name) {
my $value = $name->{$key};
print "$key => $value\n";
}
I'm getting
Not a HASH reference at ./x.pl line 19.
The tags are returned as an array, not a hash. So you're looking at doing something like this, instead, to iterate over them:
foreach my $tag (#$name) {
my $key = $tag->{Key};
my $val = $tag->{Value};
print "$key => $val\n";
}
The data structure dump of variable $name indicates that you have array reference.
You can use loop to output the data of interest, do not forget to dereference $name variable.
use strict;
use warnings;
use feature 'say';
my $name = [
{
'Key' => 'Name',
'Value' => 'fl-demo'
},
{
'Value' => 'FL',
'Key' => 'state'
}
];
say "$_->{Key} = $_->{Value}" for #$name;
Output
Name = fl-demo
state = FL
Elaborating on a previous answer:
$name is a reference to an array containing references to hashes.
#$name and #{$name} (equivalent representations) refer to the array that $name references.
${$name}[0] and $name->[0] (equivalent representations) refer to the first hash in the array referenced by $name.
${$name}[0]{'Key'}, $name->[0]->{'Key'}, etc. (equivalent representations) refer to 'Key''s hash value in the first hash in the array referenced by $name.
As such, the following would iterate over all array and hash elements:
foreach my $hashref ( #{$name} )
{
foreach my $key ( sort(keys(%{$hashref})) )
{
printf("%s => %s\n",$key,$hashref->{$key});
}
print "\n";
}
Or, more compactly (and arguably unreadably):
printf("%s\n",join("\n", map {
my $h = $_;
join(', ', map { sprintf('%s=%s',$_,$h->{$_}) } sort(keys(%{$h})) );
} #{$name} ));

Iterate through values of a HASH and convert to comma separated strings

I want to iterate through the values of a big hash, and if any of the values of that hash are keys, I want to convert it into a comma separated list which can be parsed in 'query_form'.
Right now from the data below I have:
name=Bob&surname=Whitbread&customerErrors=HASH(Xa456) (for example)
Here's what I have so far:
sub convertArgsToQueryString {
my $class = shift;
my $args = shift;
return unless ($args && ref($args) eq 'HASH');
foreach my $key (values %$args) {
if (ref($key) eq 'HASH') {
# change to a comma separated list
}
}
my $dummyURL = URI->new('', 'http');
$dummyURL->query_form(%$args);
return $dummyURL->query;
}
Data:
my $data = {
'name' => 'Bob',
'surname' => 'Whitbread',
'customerErrors' => {
'error1' => 'paymentError',
'error2' => 'addressError'
},
};
Query Form:
name=Bob&surname=Whitbread&customerErrors=paymentError,addressError
This will do what you want
print join ",", values %{$data->{customerErrors}},"\n";
Although I would suggest, rather than error1 as hash keys, you'd be better off with an array:
my $data = {
'name' => 'Bob',
'surname' => 'Whitbread',
'customerErrors' => [ 'paymentError', 'addressError' ],
};
Scaling that out to be generic, you will find the ref function to be helpful:
foreach my $key ( keys %$data ) {
print "$key is a ", ref $data->{$key},"\n";
if ( ref $data->{$key} eq 'HASH' ) {
print join ",", values %{$data->{$key}};
}
else {
print $data -> {$key},"\n";
}
}
Or tersely:
print join "\&", map { #join iterated on &
join "=", $_, #join paired values on =
ref $data->{$_} eq 'HASH' #ternary to check reference type
? values %{ $data->{$_} } #extract values if HASH
: $data->{$_} #extract just value if not.
} keys %$data; #iterate keys of data
Which gives as output:
name=Bob&customerErrors=addressError=paymentError&surname=Whitbread

how to declare array reference in hash refrence

my $memType = [];
my $portOp = [];
my $fo = "aster.out.DRAMA.READ.gz";
if($fo =~/aster.out\.(.*)\.(.*)\.gz/){
push (#{$memType},$1);
push (#{$portOp},$2);
}
print Dumper #{$memType};
foreach my $mem (keys %{$portCapability->{#{$memType}}}){
//How to use the array ref memType inside a hash//
print "entered here\n";
//cannot post the rest of the code for obvious reasons//
}
I am not able to enter the foreach loop . Can anyone help me fix it?
Sorry this is not the complete code . Please help me.
%{$portCapability->{#{$memType}}}
This doesn't do what you may think it means.
You treat $portCapability->{#{$memType}} as a hash reference.
The #{$memType} is evaluated in scalar context, thus giving the size of the array.
I aren't quite sure what you want, but would
%{ $portCapability->{ $memType->[0] } }
work?
If, however, you want to slice the elements in $portCapability, you would need somethink like
#{ $portCapability }{ #$memType }
This evaluates to a list of hashrefs. You can then loop over the hashrefs, and loop over the keys in an inner loop:
for my $hash (#{ $portCapability }{ #$memType }) {
for my $key (keys %$hash) {
...;
}
}
If you want a flat list of all keys of the inner hashes, but don't need the hashes themselves, you could shorten above code to
for my $key (map {keys %$_} #{ $portCapability }{ #$memType }) {
...;
}
I think what you want is this:
my $foo = {
asdf => {
a => 1, b => 2,
},
foo => {
c => 3, d => 4
},
bar => {
e => 5, f => 6
}
};
my #keys = qw( asdf foo );
foreach my $k ( map { keys %{ $foo->{$_} } } #keys ) {
say $k;
}
But you do not know which of these $k belongs to which key of $foo now.
There's no direct way to get the keys of multiple things at the same time. It doesn't matter if these things are hashrefs that are stored within the same hashref under different keys, or if they are seperate variables. What you have to do is build that list yourself, by looking at each of the things in turn. That's simply done with above map statement.
First, look at all the keys in $foo. Then for each of these, return the keys inside that element.
my $memType = [];
my $portOp = [];
my $fo = “aster.out.DRAMA.READ.gz”;
if ($fo =~ /aster.out\.(\w+)\.(\w+)\.gz/ ) { #This regular expression is safer
push (#$memType, $1);
push (#$portOp, $2);
}
print Dumper “#$memType”; #should print “DRAMA”
#Now if you have earlier in your program the hash %portCapability, your code can be:
foreach $mem (#$memType) {
print $portCapability{$mem};
}
#or if you have the hash $portCapability = {…}, your code can be:
foreach $mem (#$memType) {
print $portCapability->{$mem};
}
#Hope it helps

How to extract key name from a hash of hash?

I have following hash of hash :
%HoH = (
flintstones => {
husband => "fred",
pal => "barney",
},
jetsons => {
husband => "george",
wife => "jane",
his boy => "elroy",
},
simpsons => {
husband => "homer",
wife => "marge",
kid => "bart",
},
);
How to iterate over each inner hash (say flintstones) and also extract the key names (husband, pal) and corresponding vales for each such iteration?
for my $k (keys %{ $HoH{flintstones} }) {
my $v = $HoH{flintstones}{$k};
print "key is $k; value is $v\n";
}
another way would be using each
while( my($k, $v) = each %{ $HoH{flintstones} }) { ... }
for my $outer_key ( keys %HoH )
{
print "Values for inner_hash $outer_key:\n";
for my $inner_key ( keys %{$HoH{$outer_key}} )
{
print "\t'$inner_key' points to " . $HoH{$outer_key}{$inner_key} . "\n";
}
}
Because each key in the outer level points to a hash in the inner level we can use all the normal hash functions on that entry.
While it is possible to write this in a more succinct way without the double loop I prefer the nested loops for two reasons.
It is more obvious when someone else has to work on the code (including you in six months as someone else).
It makes it easier to track things such as which outer key leads to this point if needed (as shown in my output).
Just loop over the hash (by keys or values or each, depending on whether you need the keys and on taste) and then loop over the inner hashes (likewise).
So, to get all of the people described by this hash:
for (values %HoH) {
for (values $_) {
push #people, $_
}
}
Or to build a table of all the husbands, all the wives, etc.:
for my $fam (values %HoH) {
push #{$relations{$_}}, $fam->{$_} for keys $fam
}
Or to re-key the table off the husbands:
for my $fam (keys %HoH) {
$patriarchs{$HoH{$fam}{husband}}{family} = $fam;
for (keys $HoH{$fam}) {
next if $_ eq 'husband';
$patriarchs{$HoH{$fam}{husband}}{$_} = $HoH{$fam}{$_};
}
}

Perl WWW:Mechanize Accessing data in a hash reference

I have a question I'm hoping you could help with?
This is the last part I need help with in understanding hash references
Code:
my $content_lengths; # this is at the top
foreach my $url ( # ... more stuff
# compare
if ( $mech->response->header('Content-Length') != $content_length ) {
print "$child_url: different content length: $content_length vs "
. $mech->response->header('Content-Length') . "!\n";
# store the urls that are found to have different content
# lengths to the base url only if the same url has not already been stored
$content_lengths->{$url}->{'different'}->{$child_url} = $mech->response->header('Content-Length');
} elsif ( $mech->response->header('Content-Length') == $content_length ) {
print "Content lengths are the same\n";
# store the urls that are found to have the same content length as the base
# url only if the same url has not already been stored
$content_lengths->{$url}->{'equal'}->{$child_url} = $mech->response->header('Content-Length');
}
What it looked like using Data::Dumper
$VAR1 = {
'http://www.superuser.com/' => {
'difference' => {
'http://www.superuser.com/questions' => '10735',
'http://www.superuser.com/faq' => '13095'
},
'equal' => {
'http://www.superuser.com/ ' => '20892'
}
},
'http://www.stackoverflow.com/' => {
'difference' => {
'http://www.stackoverflow.com/faq' => '13015',
'http://www.stackoverflow.com/questions' => '10506'
},
'equal' => {
'http://www.stackoverflow.com/ ' => '33362'
}
}
};
What I need help with:
I need help understanding the various ways of accessing the different parts in the hash reference and using them to do things, such as print them.
So for example how do I print all the $url from the hash reference (i.e from Data::Dumper that will be http://www.superuser.com/ and http://www.stackoverflow.com/)
and how do I print all the $child_url or a particular one/subset from $child_url and so on?
Your help with this is much appreciated,
thanks a lot
You can navigate your hashref thusly:
$hashref->{key1}{key2}{keyN};
For example, if you want the superuser equal branch:
my $urlArrayref = $hashref->{'http://www.superuser.com/'}{'equal'};
More to the point, to print the urls (first level key) of the hashref, you would do:
foreach my $key ( keys( %{$hashref} ) ) {
print( "key is '$key'\n" );
}
Then if you wanted the second level keys:
foreach my $firstLevelKey ( keys( %{$hashref} ) ) {
print( "first level key is '$firstLevelKey'\n" );
foreach my $secondLevelKey ( keys( %{$hashref->{$firstLevelKey}} ) ) {
print( "\tfirst level key is '$secondLevelKey'\n" );
}
}
And so forth...
----- EDIT -----
This is working sample code from your example above:
#!/usr/bin/perl
use strict;
use warnings;
my $content_lengths = {
'http://www.superuser.com/' => {
'difference' => {
'http://www.superuser.com/questions' => '10735',
'http://www.superuser.com/faq' => '13095'
},
'equal' => {
'http://www.superuser.com/ ' => '20892'
}
},
'http://www.stackoverflow.com/' => {
'difference' => {
'http://www.stackoverflow.com/faq' => '13015',
'http://www.stackoverflow.com/questions' => '10506'
},
'equal' => {
'http://www.stackoverflow.com/ ' => '33362'
}
}
};
foreach my $key1 ( keys( %{$content_lengths} ) ) {
print( "$key1\n" );
foreach my $key2 ( keys( %{$content_lengths->{$key1}} ) ) {
print( "\t$key2\n" );
foreach my $key3 ( keys( %{$content_lengths->{$key1}{$key2}} ) ) {
print( "\t\t$key3\n" );
}
}
}
Which results in this output:
http://www.superuser.com/
difference
http://www.superuser.com/questions
http://www.superuser.com/faq
equal
http://www.superuser.com/
http://www.stackoverflow.com/
difference
http://www.stackoverflow.com/faq
http://www.stackoverflow.com/questions
equal
http://www.stackoverflow.com/