Extract data hash reference from perl array - perl

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}'";
}
}

Related

Split string into a hash of hashes (perl)

at the moment im a little confused..
I am looking for a way to write a string with an indefinite number of words (separated by a slash) in a recursive hash.
These "strings" are output from a text database.
Given is for example
"office/1/hardware/mouse/count/200"
the next one can be longer or shorter..
This must be created from it:
{
office {
1{
hardware {
mouse {
count => 200
}
}
}
}
}
Any idea ?
Work backwards. Split the string. Use the last two elements to make the inner-most hash. While more words exist, make each one the key of a new hash, with the inner hash as its value.
my $s = "office/1/hardware/mouse/count/200";
my #word = split(/\//, $s);
# Bottom level taken explicitly
my $val = pop #word;
my $key = pop #word;
my $h = { $key => $val };
while ( my $key = pop #word )
{
$h = { $key => $h };
}
Simple recursive function should do
use strict;
use warnings;
use Data::Dumper;
sub foo {
my $str = shift;
my ($key, $rest) = split m|/|, $str, 2;
if (defined $rest) {
return { $key => foo($rest) };
} else {
return $key;
}
}
my $hash = foo("foo/bar/baz/2");
print Dumper $hash;
Gives output
$VAR1 = {
'foo' => {
'bar' => {
'baz' => '2'
}
}
};
But like I said in the comment: What do you intend to use this for? It is not a terribly useful structure.
If there are many lines to be read into a single hash and the lines have a variable number of fields, you have big problems and the other two answers will clobber data by either smashing sibling keys or overwriting final values. I'm supposing this because there is no rational reason to convert a single line into a hash.
You will have to walk down the hash with each field. This will also give you the most control over the process.
our $hash = {};
our $eolmark = "\000";
while (my $line = <...>) {
chomp $line;
my #fields = split /\//, $line;
my $count = #fields;
my $h = $hash;
my $i = 0;
map { (++$i == $count) ?
($h->{$_}{$eolmark} = 1) :
($h = $h->{$_} ||= {});
} #fields;
}
$h->{$_}{$eolmark} = 1 You need the special "end of line" key so that you can recognize the end of a record and still permit longer records to coexist. If you had two records
foo/bar/baz foo/bar/baz/quux, the second would overwrite the final value of the first.
$h = $h->{$_} ||= {} This statement is a very handy idiom to both create and populate a cache in one step and then take a shortcut reference to it. Never do a hash lookup more than once.
HTH

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

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'
}
}
};

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

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
}
}

Passing arrays in Perl

All right, I'll try to explain what I've done so far. I'm using a Parellel::ForkManager to grab data from an array of URLs, which is then stored in variables (value1, value2, value3).
I then collect the data from all of those processes, and display the data with $pm->run_on_finish.
#...
my $pm = new Parallel::ForkManager(10);
$pm->run_on_finish (
sub {
my #info = #{$data_structure_reference};
print $info[0];
print $info[1];
print $info[2];
}
);
for my $var (#urls) {
$pm->start and next;
#...
#returned = &something($var);
#...
$pm->finish(0, \#returned);
}
sub something {
#... getting data from each URL and storing it in variables
my #array = (
$value1,
$value2,
$value3
);
return #array;
}
Now, what I want to do, is to pass an array, #value4, as well, and then only display that data if there is something in the array. So, I want it to look like this:
sub something {
#... getting data from each URL and storing it in variables
my #array = (
$value1,
$value2,
$value3,
#value4
);
return #array;
}
And then I want it to print that array, only if there is something in it.
Unfortunately, I'm not entirely sure how to go about doing that.
I assume that what you are asking is how to return an array along with the three scalars returned from the something() sub, and print it?
I also assume that those three scalars are what's referred to as being in #info.
The simplest way seems to me to be to simply tack them to the end of the array you return, use the three first values, and if there's anything left, print that too.
$pm->run_on_finish (
sub {
my #info = #{$data_structure_reference};
print splice #info, 0, 3;
print #info if (#info);
}
);
sub something {
return (
$value1,
$value2,
$value3,
#value4
);
}
As you'll notice, you do not need to fill a dummy array for the return value, simply return values inside the parens. You do not need to dereference the array, since you can use the #info array straight up if you splice off the first three values.
I like it simple. If it works.
I've previously provided a simple solution to this problem. It can use threads (use threads;) or processes (use forks;).
use threads; # or: use forks;
use Thread::Queue qw( );
use constant NUM_WORKERS => 10;
my $request_q = Thread::Queue->new();
my $response_q = Thread::Queue->new();
# Create the workers.
for (1..NUM_WORKERS) {
(async {
while (defined(my $request = $request_q->dequeue())) {
$response_q->enqueue(process_request($request));
}
})->detach();
}
# Submit work to workers.
$request_q->enqueue(#requests);
# Signal the workers they are done.
$request_q->enqueue(undef) for 1..NUM_WORKERS;
# Collect the results.
my $working = NUM_WORKERS;
while ($working) {
my $result = $response_q->dequeue();
if (!defined($result)) {
--$working;
next;
}
process_response($result);
}
The work to be done in the children is done by process_request.
sub process_request {
# ...
return [
$value1,
$value2,
$value3,
\#value4,
];
}
The results are passed to process_response in the parent.
sub process_response {
my ($value1, $value2, $value3, $value4) = #{ $_[0] };
...
}
I am not totally sure what you are asking, but in order to pass multiple arrays to a function in Perl one must pass by reference.
my #array1 = (1, 2, 3);
my #array2 = ('a', 'b', 'c');
&fn(\#array1, \#array2);
In order to print an array only in the case when it has value, one must simply check that it has value and print it:
print "#array" if #array;
Though, the nice function of "#array" is that if #array has no value then "#array" evaluates to "". This allows you to reduce the previous statement to a simple:
print "#array"