Perl getting hash from reference to an array containing a reference - perl

I have the following code that returns a reference to an array with one element. That element contains a reference to a hash. I would like to eliminate this double referencing and work either with a copy of the hash or directly on the hash itself so I can use the keys.
Here's the code:
my $extracted = $db_obj->extract('source_tag', $source_tag);
So $extracted contains a reference to an array. That array has a single element, a reference to a hash. The hash looks like this when I use Data::Dumper:
$VAR1 = \\[
{
'data_center' => 'qe76',
'description' => 'locator',
'abs_delta_dollar_percent' => undef,
'content_type' => 'Raw',
'source_tag' => 'hg9efx4',
'producer' => 'partner',
'id' => '15282',
'storage_type' => 'box',
'storage_path' => '/mnt/storage/2012'
}
];
I'd like to be able to directly access the hash so I can use the keys to grab the values. How do I remove this double referencing?

my %hash = %{ $db_obj->extract('source_tag', $source_tag)->[0] } ;

my $extracted = $db_obj->extract('source_tag', $source_tag) -> [0];

That seems to be:
$$$$result[0]
or
$$$result[0]
if you didn't mean that double backslash.

Related

How to get the hash keys and values of a hash in perl mojo

I try to figure out how to select explicit key and values of a hash.
my $hash = query('select id, name, required from table')->hashes
My output when I dump is:
var1 = bless ([
{
'name' => value,
'id' => value,
'required' => value
}.....
])
What I want is to get the follow output:
var1 = bless ([
{
'required' => value
}...
])
After that I want to compare if the index == index of another array.
You removed the class name from bless, but I guess it's Mojo::Collection. Use it's map method to iterate over the elements:
my $required = $hash->map(sub { required => $_->{required} });
# Untested.
Also, using the name $hash for something that's a collection of hashes is confusing.
I don't understand your last sentence about the index. If you want to extract the $index-th element, you can use
my $hash = $required->to_array->[$index];
or to get directly the value
my $value = $required->to_array->[$index]{required};
Once you get a hash reference, from whatever means, you access the keys like this:
$hash->{required}; # etc.
See perlref for more information. Also read perldsc and perlreftut.

How to merge two hashes returned from sub, in Perl

I am trying to merge two hashes. But they are return values from functions. How can I dereference the return values inline? I don't want to use extra variables such as my $pos = makePos();
use v5.8.8;
use strict;
use warnings;
sub _makePos
{
my $desc= {
pos50 => {unit => 'm', desc => 'position error' },
pos95 => {unit => 'm', desc => '95% position error' }
};
return $desc;
}
sub _makeVel
{
my $desc= {
vel50 => {unit => 'm/s', desc => 'velocity error' },
vel95 => {unit => 'm/s', desc => '95% velocity error' }
};
return $desc;
}
my $descs = {_makePos(), _makeVel()};
use Data::Dumper;
print Dumper($descs);
this prints only the hash returned from _makeVel. how does it work?
$VAR1 = {
'HASH(0x21ea4a0)' => {
'vel50' => {
'desc' => 'velocity error',
'unit' => 'm/s'
},
'vel95' => {
'unit' => 'm/s',
'desc' => '95% velocity error'
}
}
};
changing this line as
my $descs = {%{_makePos()}, %{_makeVel()}};
worked!
Actually, your original solution did print both of the hashes, but the first one was "stringified" as it was being used as the key of your hash. It's there as HASH(0x21ea4a0).
I see you have a solution, but it might be worth explaining what was going wrong and why your solution fixed it.
Your two subroutines don't return hashes but, rather, hash references. A hash reference is a scalar value that is, effectively, a pointer to a hash.
A hash is created from a list of values. Your code that creates your news hash (actually, once again, a hash reference) is this:
my $descs = {_makePos(), _makeVel()};
This is two scalar values. The first is used as a key in the new hash and the second is used as the associated value - hence the results you get from Data::Dumper.
What you actually want to do is to "dereference" your hashes and get back to the actual hashes. You can dereference a hash using the syntax %{ ... }, where the ... is any expression returning a hash reference. And that's what you've done in your solution. You dereference the hash references, which gives you a list of key/value pairs. The pairs from the two dereferenced hashes are then joined together in a single list which is used to create your new, combined, hash.
I should point out that there's a danger in this approach. If your two subroutines can ever return references to hashes that contain the same key, then only one version of that repeated key will appear in the combined hash.

How to get the key associated with a hash reference's key in a hash of hashes?

In an attempt to help me learn Perl, I built the following data structure, where the inner hash (/DriveA/archive, etc.) is a hash reference:
#The contents of the %properties hash of hashes
#The inner hash is a hash reference to a hash named %attributes
$VAR1 = {
'/DriveA' => {
'/DriveA/archive/' => {
'MaxSize' => '20GB',
'Size' => '19GB',
'Free' => '5'
},
'/DriveA/current/' => {
'MaxSize' => '20GB',
'Size' => '12GB',
'Free' => '40'
}
},
'/DriveB' => {
'/DriveB/archive/' => {
'MaxSize' => '8GB',
'Size' => '6GB',
'Free' => '25'
},
'/DriveB/current/' => {
'MaxSize' => '80GB',
'Size' => '20GB',
'Free' => '75'
}
},
'/DriveC' => {
'/DriveC/' => {
'MaxSize' => '20GB',
'Size' => '10GB',
'Free' => '50'
}
}
}
I created an array to hold the keys for %attributes (aka the values/hash reference in %properties) using:
#list = sort keys %attributes;
I'm trying to iterate over the elements in #list and find the associated key for the outer hash in %properties. So, for example, if /DriveA/archive/ is the next item in the array, I want to find the hash key associated with that value, /DriveA, from %properties, assuming dereference of the inner hash.
I created a reverse hash, which outputs the following...
$VAR1 = {
'HASH(0x2002f244)' => '/DriveB',
'HASH(0x2002f388)' => '/DriveC',
'HASH(0x2002f1e4)' => '/DriveA'
}
...using this code...
foreach my $item (#list) {
my %rhash = reverse %properties; # Reverse the hash of hashes so value is key
print Dumper(\%rhash);
}
Question 1:
Given the above, how would I dereference the hash so I can find $item in the hash reference so I can determine the associated value (not the hash reference value).
If $item = '/DriveA/archive/', I want to capture '/DriveA' in a variable from %properties so it can be returned from a subroutine.
I know the inner hash needs to be dereferenced, I'm just not sure how to do it. I've read through perlref, perldsc, and perllol, but I haven't been able to find the answer.
Thanks.
The easiest thing to do is to just generate the reverse keys directly by traversing the data structure:
my %reverse_keys;
foreach my $outer_key (keys %properties) {
my $inner_hashref = $properties->{$outer_key};
my %reverse_map = map { ($_ => $outer_key) } keys %$inner_hashref;
%reverse_keys = (%reverse_keys, %reverse_map);
}
Ideally, you can even generate %reverse_keys directly at the same time you're stashing data into %properties, so you don't need that extra traversal code above.
To address your actual technical question:
You are starting with a hash reference (basically, a pointer in C vernacular).
Then you assign that hash reference as a key into a hash.
Assigning something as a hash key puts it into a string context (a special case of scalar context).
When you stringify a hash reference in Perl, it gets turned into a string representation of that reference - that's your HASH(0x2002f244) string you see when you Data::Dumper your reverse hash.
What you're asking is, on a technical level, is "how do I convert the string representation of a hash reference back into the hash reference itself?"
This is covered in Perl FAQ 4. As far as I'm aware, you can NOT easily convert the string representation of a hash reference back into the hash reference itself.
If you absolutely must (which I strongly recommend against - instead, use the solution at the top of the answer) - you can do so using Devel::Pointer CPAN module.
A solution using that module is shown in this PerlMonks thread.
An additional solution may be to use Tie::RefHash module to use actual hash references instead of their string representations as keys. This is documented in Chapter 8.5.1. "References Don't Work as Hash Keys" of O'Reilly's "Programming Perl, 3rd edition". I would recommend against that bit of madness as well.

Convert array of hashes to json

I want to convert an array of hashes that I create like this:
while(...)
{
...
push(#ranks, {id => $id, time => $time});
}
To JSON:
use JSON;
$j = new JSON;
print $j->encode_json({ranks => #ranks});
But it is outputting this:
{"ranks":{"time":"3","id":"tiago"},
"HASH(0x905bf70)":{"time":"10","id":"bla"}}
As you can see it isnt able to write on of the hashes and there's no array...
I would like to output a JSON string that looked like this:
{"ranks":[{"time":"3","id":"tiago"},
{"time":"40","id":"fhddhf"},
{"time":"10","id":"bla"}]}
All of these are the same:
ranks => #ranks
'ranks', #ranks
'ranks', $ranks[0], $ranks[1], $ranks[2]
ranks => $ranks[0], $ranks[1] => $ranks[2]
So you're creating a hash with two elements when you mean to create a hash with one element.
You tried to use an array as a hash value, but hash values can only be scalars. It is common, however, to use a reference to an array as a hash value since references are scalars, and this is what encode_json expects.
print $j->encode_json( { ranks => #ranks } );
should be
print $j->encode_json( { ranks => \#ranks } );
print $j->encode_json({ranks => #ranks});
should be:
print $j->encode_json({ranks => \#ranks});
Try passing the array as a reference.
to_json({ranks => \#ranks},{ascii => 1,pretty => 1});

Curly braces for a hash

Which structure is created as following in Perl?
my $self = { Name => $name, Color => $class->default_color };
If it is a hash, then is the official notation not the following ( parentheses, % instead of $):
my %self = ( Name => $name, Color => $class->default_color );
The data in { ... } is a hash ref.
The data in ( ... ) is a list, but the context makes it into a hash.
Well, it's still a hash - but an anonymous one. And its reference is assigned to $self. The doc says:
A reference to an anonymous hash can be created using curly brackets:
$hashref = {
'Adam' => 'Eve',
'Clyde' => 'Bonnie',
};
Perl doesn't have a literal representation of a hash, so we create a hash as a list of key-value pairs. The anonymous hash constructor or assignment to named hash converts the list of key-value pairs to a hash.
The top line creates a hash reference which you assign to a scalar variable:
my $self = { Name => $name, Color => $class->default_color };
The bottom line assigns a list to a named hash:
my %self = ( Name => $name, Color => $class->default_color );