How do you access information in a hash reference that has been passed to a sub-routine? - perl

I am trying to use hash references to pass information to sub-routines. Psuedo code:
sub output_detail {
Here I want to be able to access each record by the key name (ex. "first", "second", etc)
}
sub output_records {
I want to use a foreach to pass each record has reference to another sub-routine
that handles each record.
foreach $key ( sort( keys %someting) ) {
output_detail(something);
}
}
%records = ();
while ($recnum, $first, $second, $third) = db_read($handle)) {
my %rec = ("first"=>$first, "second"=>$second, "third=>$third);
my $id = $recnum;
$records{$id} = \%rec;
}
output_records(\%records);
I'm not sure how to de-reference the hashes when passed to a sub-routine.
Any ideas would be very helpful.
Thanks

Use -> to access keys of a hash ref. So, your argument to output_records will come through as a scalar hash ref.
sub output_records {
my $records = shift;
my $first = $records->{"first"};
}
See perlreftut for more info.

sub output_detail {
my $hash = shift;
my $value = $$hash{some_key};
}
sub output_records {
my $hash = shift;
foreach my $key (sort keys %$hash) {
output_detail($hash, $key);
# or just pass `$$hash{$key}` if you only need the value
}
}

Related

Extract data hash reference from perl array

I am new to perl. I am returning an array of references from a function. However I am lost as how I loop over the data.
sub whatever{
my %product;
my %resolution;
my #data = ();
push #data, \%product;
push #data, \%resolution;
return #data;
}
In the control sub module.
my #results = $whatever($dt_id);
$c->app->log->debug(Dumper(#results));
results
$VAR1 = {'IOP' => 'IOP'};
$VAR2 = {'4km' => '4km','9km' => '9km'};
I get the two hashes but how do I loop over them.
To return the two hashes seperately:
return (\%product, \%resolution);
To map over them:
my ($product, $resolution) = whatever(...);
for my $key (%$product) {
# do something with $product->{$key};
}
try this:
for my $hash_ref (#results) {
for my $key (keys %$hash_ref) {
say "key '$key' has value '$hash_ref->{$key}'";
}
}

Passing variables to a Perl subroutine

I would like to pass the following variables to subroutine mySubroutine, $name, $age and then this multidimensional array:
$name = "jennifer";
$age = 100;
$list[0][0] = "TEST NAME 2";
$list[0][1] = "TEST GROUP 2";
$[0][2] = 10;
$[1][0] = "TEST NAME 2";
$[1][1] = "TEST GROUP 2";
$[1][2] = 2;
Subroutine:
sub mySubroutine
{
}
I have tried $_[0], and #_, but I don't seem to get the variables passed to the subroutine correctly.
There are several ways to do it (like most things in Perl). I personally do it like this:
sub mySubroutine
{
# Get passed arguments
my ($name, $age, $refList) = #_;
# Get the array from the reference
my #list = #{$refList};
# Good to go
}
# You need to pass #list as reference, so you
# put \#list, which is the reference to the array
mySubroutine($name, $age, \#list);
Another option, as long as you are only passing one array, is to pass it normally by value as the last element:
sub scalars_and_one_array {
my $name = shift;
my $age = shift;
foreach my $element (#_)
{
# Do something with the array that was passed in.
}
}
scalars_and_one_array($name,$age,#array);
However, it is most efficient to avoid any additional copy of the array by only using a reference within the sub. This does mean that changes to the array in the sub affect the original, however:
sub array_by_ref {
my $array_ref = shift;
foreach my $element (#$array_ref)
{
# Changing $element changes #original_array!
}
}
array_by_ref(\#original_array);
Another way, which passes the array by reference, but then makes a copy of it to avoid changing the original when you edit it.
sub mySubroutine
{
## Retrieve name
my $name = shift;
## Retrieve age
my $age = shift;
## Retrieve list reference
my $refList = shift;
## De-reference the list's scalar
my #list = #{$refList};
# Good to go
}
## Function call
mySubroutine($name, $age, \#list);
For a better understanding, please refer to perlsub (Perl subroutines).

Perl: How to turn array into nested hash keys

I need to convert a flat list of keys into a nested hash, as follow:
my $hash = {};
my #array = qw(key1 key2 lastKey Value);
ToNestedHash($hash, #array);
Would do this:
$hash{'key1'}{'key2'}{'lastKey'} = "Value";
sub to_nested_hash {
my $ref = \shift;
my $h = $$ref;
my $value = pop;
$ref = \$$ref->{ $_ } foreach #_;
$$ref = $value;
return $h;
}
Explanation:
Take the first value as a hashref
Take the last value as the value to be assigned
The rest are keys.
Then create a SCALAR reference to the base hash.
Repeatedly:
Dereference the pointer to get the hash (first time) or autovivify the pointer as a hash
Get the hash slot for the key
And assign the scalar reference to the hash slot.
( Next time around this will autovivify to the indicated hash ).
Finally, with the reference to the innermost slot, assign the value.
We know:
That the occupants of a hash or array can only be a scalar or reference.
That a reference is a scalar of sorts. (my $h = {}; my $a = [];).
So, \$h->{ $key } is a reference to a scalar slot on the heap, perhaps autovivified.
That a "level" of a nested hash can be autovivified to a hash reference if we address it as so.
It might be more explicit to do this:
foreach my $key ( #_ ) {
my $lvl = $$ref = {};
$ref = \$lvl->{ $key };
}
But owing to repeated use of these reference idioms, I wrote that line totally as it was and tested it before posting, without error.
As for alternatives, the following version is "easier" (to think up)
sub to_nested_hash {
$_[0] //= {};
my $h = shift;
my $value = pop;
eval '$h'.(join '', map "->{\$_[$i]}", 0..$#_).' = $value';
return $h;
}
But about 6-7 times slower.
I reckon this code is better - more amenable to moving into a class method, and optionally setting a value, depending on the supplied parameters. Otherwise the selected answer is neat.
#!/usr/bin/env perl
use strict;
use warnings;
use YAML;
my $hash = {};
my #array = qw(key1 key2 lastKey);
my $val = [qw/some arbitrary data/];
print Dump to_nested_hash($hash, \#array, $val);
print Dump to_nested_hash($hash, \#array);
sub to_nested_hash {
my ($hash, $array, $val) = #_;
my $ref = \$hash;
my #path = #$array;
print "ref: $ref\n";
my $h = $$ref;
$ref = \$$ref->{ $_ } foreach #path;
$$ref = $val if $val;
return $h;
}
Thxs for the good stuff!!!
I did it the recursive way:
sub Hash2Array
{
my $this = shift;
my $hash = shift;
my #array;
foreach my $k(sort keys %$hash)
{
my $v = $hash->{$k};
push #array,
ref $v eq "HASH" ? $this->Hash2Array($v, #_, $k) : [ #_, $k, $v ];
}
return #array;
}
It would be interesting to have a performance comparison between all of these solutions...
Made a better version of axeman's i think. Easier to understand without the -> and the \shift to me at least. 3 lines without a subroutine.
With subroutine
sub to_nested_hash {
my $h=shift;
my($ref,$value)=(\$h,pop);
$ref=\%{$$ref}{$_} foreach(#_);
$$ref=$value;
return $h;
}
my $z={};
to_nested_hash($z,1,2,3,'testing123');
Without subroutine
my $z={};
my $ref=\$z; #scalar reference of a variable which contains a hash reference
$ref=\%{$$ref}{$_} foreach(1,2,3); #keys
$$ref='testing123'; #value
#with %z hash variable just do double backslash to get the scalar reference
#my $ref=\\%z;
Result:
$VAR1 = {
'1' => {
'2' => {
'3' => 'testing123'
}
}
};

compare multiple hashes for common keys merge values

I have a working bit of code here where I am comparing the keys of six hashes together to find the ones that are common amongst all of them. I then combine the values from each hash into one value in a new hash. What I would like to do is make this scaleable. I would like to be able to easily go from comparing 3 hashes to 100 without having to go back into my code and altering it. Any thoughts on how I would achieve this? The rest of the code already works well for different input amounts, but this is the one part that has me stuck.
my $comparison = List::Compare->new([keys %{$posHashes[0]}], [keys %{$posHashes[1]}], [keys %{$posHashes[2]}], [keys %{$posHashes[3]}], [keys %{$posHashes[4]}], [keys %{$posHashes[5]}]);
my %comboHash;
for ($comparison->get_intersection) {
$comboHash{$_} = ($posHashes[0]{$_} . $posHashes[1]{$_} . $posHashes[2]{$_} . $posHashes[3]{$_} . $posHashes[4]{$_} . $posHashes[5]{$_});
}
my %all;
for my $posHash (#posHashes) {
for my $key (keys(%$posHash)) {
push #{ $all{$key} }, $posHash->{$key};
}
}
my %comboHash;
for my $key (keys(%all)) {
next if #{ $all{$key} } != #posHashes;
$comboHash{$key} = join('', #{ $all{$key} });
}
Just make a subroutine and pass it hash references
my $combination = combine(#posHashes);
sub combine {
my #hashes = #_;
my #keys;
for my $href (#hashes) {
push #keys, keys %$href;
}
# Insert intersection code here..
# .....
my %combo;
for my $href (#hashes) {
for my $key (#intersection) {
$combo{$key} .= $href->{$key};
}
}
return \%combo;
}
Create a subroutine:
sub combine_hashes {
my %result = ();
my #hashes = #_;
my $first = shift #hashes;
for my $element (keys %$first) {
my $count = 0;
for my $href (#hashes) {
$count += (grep {$_ eq $element} (keys %$href));
}
if ($count > $#hashes) {
$result{$element} = $first->{$element};
$result{$element} .= $_->{$element} for #hashes;
}
}
\%result;
}
and call it by:
my %h = %{combine_hashes(\%h1, \%h2, \%h3)};
...or as:
my %h = %{combine_hashes(#posHashes)};
There is pretty straightforward solution:
sub merge {
my $first = shift;
my #hashes = #_;
my %result;
KEY:
for my $key (keys %$first) {
my $accu = $first->{$key};
for my $hash (#hashes) {
next KEY unless exists $hash->{$key};
$accu .= $hash->{$key};
}
$result{$key} = $accu;
}
return \%result;
}
You have to call it with references to hashes and it will return also hash reference e.g.:
my $comboHashRef = merge(#posHashes);

How to construct a hash of hashes

I need to compare two hashes, but I can't get the inner set of keys...
my %HASH = ('first'=>{'A'=>50, 'B'=>40, 'C'=>30},
'second'=>{'A'=>-30, 'B'=>-15, 'C'=>9});
foreach my $key (keys(%HASH))
{
my %innerhash = $options{$key};
foreach my $inner (keys(%innerhash))
{
print "Match: ".$otherhash{$key}->{$inner}." ".$HASH{$key}->{$inner};
}
}
$options{$key} is a scalar (you can tell be the leading $ sigil). You want to "dereference" it to use it as a hash:
my %HASH = ('first'=>{'A'=>50, 'B'=>40, 'C'=>30},
'second'=>{'A'=>-30, 'B'=>-15, 'C'=>9});
foreach my $key (keys(%HASH))
{
my %innerhash = %{ $options{$key} }; # <---- note %{} cast
foreach my $inner (keys(%innerhash))
{
print "Match: ".$otherhash{$key}->{$inner}." ".$HASH{$key}->{$inner};
}
}
When you're ready to really dive into these things, see perllol, perldsc and perlref.
I'm guessing you say "options" there where you mean "HASH"?
Hashes only store scalars, not other hashes; each value of %HASH is a hash reference that needs to be dereferenced, so your inner loop should be:
foreach my $inner (keys(%{ $HASH{$key} })
Or:
my %HASH = ('first'=>{'A'=>50, 'B'=>40, 'C'=>30},
'second'=>{'A'=>-30, 'B'=>-15, 'C'=>9});
foreach my $key (keys(%HASH))
{
my $innerhash = $HASH{$key};
foreach my $inner (keys(%$innerhash))
{
print "Match: ".$otherhash{$key}->{$inner}." ".$innerhash->{$inner};
}
}