Convert Hash To Array in Perl Catalyst - perl

I need help regarding handling of Perl variables. Here I am getting input as a hash. I now need to send this hash variable to another subroutine. How can pass data as an argument to another subroutine? The code below shows how I am approaching this:
if ($csData->{'CUSTOMER_INVOICE_DETAILS'})
{
$c->log->debug("API Response:". Dumper $csData->{'CUSTOMER_INVOICE_DETAILS'});
my $Charges = [];
my #customerCharges = $csData->{'CUSTOMER_INVOICE_DETAILS'};
foreach(#customerCharges)
{
my ($customername,$customeramount) = split /:/;
my $charge_hash = ({
customername => $customername,
customeramount => $customeramount
});
push(#$Charges, $charge_hash);
}
my #ReturnCharges = $self->API->get_customer_charges($Charges, $Customer->customerid, $params->{'invoiceid'});
The other subroutine where this data is being received is as follows:
sub get_customer_charges
{
my $self = shift;
my ($charge, $CustomerId, $INID) = #_;
my $http_request = {
action => 'GetTariff',
customerid => $CustomerId,
csid => $INID,
};
my $markups = $self->APIRequest($http_request);
###Charge Level ID Inserting As 10
my #ChargeLevels;
my #BaseLevelID;
foreach my $ch (#$charge)
{
my ($customername,$customeramount) = split(':', $ch->{'customername'}, $ch->{'customername'});
my $chargelevel = join(':', $ch->{'customername'}, $ch->{'customeramount'}, '10');
push(#BaseLevelID, $chargelevel);
}
push(#ChargeLevels, #BaseLevelID);
return #ChargeLevels;
}
When I print to the server log for CUSTOMER_INVOICE_DETAILS variable I am getting the following values:
API Response:$VAR1 = {
'Product' => '34.04',
'basetax' => '2.38',
'vattax' => '4.36'
};
After sending data to second subroutine the data coming in server log for second subroutine variable is as following:
Charges in API:$VAR1 = 'HASH(0xb75d6d8)::10';
Can anyone help how could I send the hash data from one subroutine to another?

Given your comments and that your source is:
API Response:$VAR1 = {
'Product' => '34.04',
'basetax' => '2.38',
'vattax' => '4.36'
};
And you're looking for:
API Response:$VAR1 = { 34.04:2.38:4.36:10 };
(and somehow you're getting:
Charges in API:$VAR1 = 'HASH(0xb75d6d8)::10';
This suggests this may be as simple as using the values system call. values extracts an array of all the values in the hash. Something like this (guessing a bit on which part of your code needs it).
my #list_of_values = values ( %{$csData->{'CUSTOMER_INVOICE_DETAILS'}} );

You say you want to "convert" a hash to an array, but your issue seems more complex and subtle so simple conversion is not likely what will solve your problem. Something in your subroutine is returning a hash reference when the rest of your code does not expect it to do so. If the data-structure you are passing contains the correct information but not in the form you expect, then you can either change the code to produce it in the expected form (e.g. to return an ARRAY) or change your subroutine so that it is able to handle the data that it is passed correctly.
As for "converting a hash" per se, if your data structure doesn't contain complex nested references and all you want to do is "convert" your hash to an array or list, then you can simply assign the hash to an array. Perhaps I'm not understanding your question but if this kind of simple "flattening" is all you want then you could try:
my $customer_purchase = {
'Product' => '34.04',
'basetax' => '2.38',
'vattax' => '4.36'
};
my #flat_customer_purchase = %{ $customer_purchase };
say "#flat_customer_purchase" ;
Output:
basetax 2.38 Product 34.04 vattax 4.36
You can then supply the hash data as the "array" to the second subroutine. e.g. treat #flat_customer_purchase as a list:
use List::AllUtils ':all';
say join " ", pairkeys #flat_customer_purchase
# basetax Product vattax
say join " ", pairvalues #flat_customer_purchase
# 2.38 34.04 4.36
etc.
NB: this assumes that for some reason you must pass an array. The example of running the pairvalues routine simply replicates #Sobrique's suggestion to use values directly on the hash you are passing but in my answer this grabs the values pairs from the array instead of the hash.
My sense is that there is more to the question. If API Response is a more complicated hash/object or, if for some other reason this basic perl doesn't work, then you will have to supply more information. You need to find out where your unexpected hash reference is coming from before you can decide how to handle it. You might find this SO discussion helpful:
Are Perl subroutines call-by-reference or call-by-value?

Related

perl hash ref return: { 'a' =>1, % { sub() } } [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
{ 'a' =>1, % { sub() } }
Is this best for including a hash returned as ref into other?
%$sub() does not work.
As far I new {} makes new hash and returns reference, probably new optimized away this case, but not sure.
I'm guessing that you have some subroutine that returns a hash reference, and that you want to include all of those keys in a larger hash:
my $hash_ref = some_sub(...);
my $big_hash = { a => 1 };
There are a couple of ways that you can do. In you question, it looks like you are trying to do it inline by dereferencing the return value. That can be reasonable. The circumfix notation or postfix dereference can do this:
# general circumfix (old way)
my $big_hash = { a => 1, %{ some_sub() } };
# postfix dereference (new v5.24 hotness)
my $big_hash = { a => 1, some_sub()->%* };
However, I tend to not like to do this so directly. Typically I'm doing this when there are default values that might be overridden by whatever some_sub() is:
my %defaults = ( ... );
my %big_hash = ( %defaults, some_sub()->%* );
But, I usually go a step further by making the thing that produces the defaults into another subroutine so I can give subclassers a way to override it:
sub defaults {
my %defaults = ( ... );
return \%defaults;
}
my %big_hash = ( defaults()->%*, some_sub()->%* );
There are many other ways to merge hashes. There's How can I combine hashes in Perl? on StackOverflow, but also How do I merge two hashes in perlfaq4.
But, there's another thing to consider. Simply mashing two hashes together to get a new one might be expensive. What if the first hash is very, very large?
my %grand_hash = ( %very_big_hash, %new_hash );
People often do this and assign back to the starting hash, mostly because it's easy to type:
my %grand_hash = ( %grand_hash, %new_hash );
You're telling Perl to unroll %grand_hash, combine another list with it, then re-hash the huge list.
Although a bit more unwieldy, a better way is to add the new keys and values.
foreach my $new_key ( keys %new_hash ) {
$grand_hash{$new_key} = $new_hash{$new_key};
}
That's nice when you need to do something else, such as skipping keys that you already have in the hash:
foreach my $new_key ( keys %new_hash ) {
next if exists $grand_hash{$new_key};
$grand_hash{$new_key} = $new_hash{$new_key};
}
or maybe adding to a value that is already there instead of replacing it:
foreach my $new_key ( keys %new_hash ) {
$grand_hash{$new_key} += $new_hash{$new_key};
}
If you just need to add it and don't care about replacing values, a hash slice is nice for assigning multiple keys and values at the same time:
#grand_hash{ keys %new_hash } = values %new_hash;
In your case, you'd call the subroutine once and store the result so you don't have to construct the hash again. You then dereference that hash is the slice (or wherever you want to use it:
my $new_hash = some_sub(...);
#grand_hash{ keys %$new_hash } = values %$new_hash;
Yes, to dereference a hash reference and get a list of key/value pairs, wrap %{ ... } around the expression that generates the hash reference.
Or, since perl 5.24, you can use the postfix dereference syntax sub()->%*

Perl return list of array refs of unknown length

I have a sub in Perl that needs to return a list of array refs to fit in with the rest of the package. The problem is that I don't know in advance how many array refs I will generate. My usual method of pushing the array refs that I generate into an array and returning a reference to that doesn't work with the rest of the code, which I can't change without breaking some legacy stuff.
sub subTrackTable {
my ($self, $experimentName, $subTrackAttr) = #_;
# return nothing if no subtracks required
if ($subTrackAttr eq 'no_sub') {
return;
}
# get distinct values for subtrack attr (eg antibody) from db
my $dbh = $self->dbh();
my $sh = $dbh->prepare("SELECT DISTINCT * blah sql");
$sh->execute();
my #subtrackTable;
while (my ($term, $value) = $sh->fetchrow_array()) {
my $subtrack = [':$value', $value];
push (#subtrackTable, $subtrack);
}
$sh->finish();
# this is hard-coded for one experiment and does what I want
# Want to loop through #subtrackTable and return a list of all the array refs it contains
# Returning nested array refs doesn't work with external code
return ([":H3K4me3", "H3K4me3"],[":H4K20me3", "H4K20me3"]);
}
The problem is that because I am dynamically getting values from a database, I don't know how many there will be. Just returning \#subtrackTable, which would be my usual strategy breaks the rest of the code. If I knew in advance how many there would be I could also do something like
my $a1 = [":$value1", $value1];
my $a2 = [":$value2", $value2];
...
my $an = [":$valuen", $valuen];
return($a1, $a2,...$an);
but I can't see how to make this work with an unknown number of arrayrefs.
Help appreciated!
It looks like you just need to
return #subtrackTable;
Also, this line
my $subtrack = [':$value', $value];
must be changed to use double quotes, like this
my $subtrack = [ ":$value", $value ];

Convert a DBIx::Class::Result into a hash

Using DBIx::Class, I found a solution to my issue, thankfully. But I'm sure there has to be a nicer way.
my $record = $schema->resultset("food")->create({name=>"bacon"});
How would I turn this record into a simple hashref instead of having to make this call right after.
my record = $schema->resultset("food")->search({name=>"bacon"})->hashref_array();
Ideally I want to be able to write a code snippet as simple as
{record=> $record}
instead of
{record => {name => $record->name, $record->food_id, ...}}
This would drive me insane with a table that has alot more columns.
I assume you're talking about DBIx::Class?
my $record = $schema->resultset("food")->create({name=>"bacon"});
my %record_columns = $record->get_columns;
# or, to get a HashRef directly
my $cols = { $record->get_columns };
# or, as you've asked for
my $foo = { record => { $record->get_columns } };
What you're looking for is included in DBIx::Class as DBIx::Class::ResultClass::HashRefInflator.

Perl nesting hash of hashes

I'm having some trouble figuring out how to create nested hashes in perl based on the text input.
i need something like this
my % hash = {
key1 => \%inner-hash,
key2 => \%inner-hash2
}
However my problem is I don't know apriori how many inner-hashes there would be. To that end I wrote the following piece of snippet to test if a str variable can be created in a loop and its reference stored in an array and later dereferenced.
{
if($line =~ m/^Limit\s+$mc_lim\s+$date_time_lim\s+$float_val\s+$mc\s+$middle_junk\s+$limit \s+$value/) {
my $str = $1 . ' ' . $2 . ' ' . $7;
push (#test_array_reference, \$str);
}
}
foreach (#test_array_reference) {
say $$_;
}
Perl dies with a not a scalar run-time error. I'm a bit lost here. Any help will be appreciated.
To answer your first (main?) question, you don't need to know how many hashes to create if you walk through the text and create them as you go. This example uses words of a string, delimited by spaces, as keys but you can use whatever input text for your purposes.
my $text = 'these are just a bunch of words';
my %hash;
my $hashRef = \%hash; # create reference to initial hash
foreach (split('\s', $text)){
$hashRef->{$_} = {}; # create anonymous hash for current word
$hashRef = $hashRef->{$_}; # walk through hash of hashes
}
You can also refer to any arbitrary inner hash and set the value by,
$hash{these}{are}{just}{a}{bunch}{of}{words} = 88;
$hash{these}{are}{just}{a}{bunch}{of}{things} = 42;
$hash{these}{things} = 33;
To visualize this, Data:Dumper may help,
print Dumper %hash;
Which generates,
$VAR1 = 'these';
$VAR2 = {
'things' => 33,
'are' => {
'just' => {
'a' => {
'bunch' => {
'of' => {
'things' => 42,
'words' => 88
}
}
}
}
}
};
my $hashref = { hash1 => { key => val,... },
hash2 => { key => val,..} };
also you may want to use the m//x modifier with your regex, its barely readable as it is.
Creating a hash of hashes is pretty simple:
my %outer_hash = {};
Not entirely necessary, but this basically means that each element of your hash is a reference to another hash.
Imagine an employee hash keyed by employee number:
$employee{$emp_num}{first} = "Bob";
$employee{$emp_num}{last} = "Smith";
$employee{$emp_num}{phones}{cell} = "212-555-1234";
$employee{$emp_num}{phones}{desk} = "3433";
The problem with this notation is that it gets rather hard to read after a while. Enter the arrow notation:
$employee{$emp_num}->{first} = "Bob";
$employee{$emp_num}->{last} = "Smith";
$employee{$emp_num}->{phones}->{cell} = "212-555-1234";
$employee{$emp_num}->{phones}->{desk} = "3433";
The big problem with complex structures like this is that you lose the use strict ability to find errors:
$employee{$emp_num}->{Phones}->{cell} = "212-555-1234";
Whoops! I used Phones instead of phones. When you start using this type of complex structure, you should use object oriented syntax. Fortunately, the perlobj tutorial is pretty easy to understand.
By the way, complex data structure handling and the ability to use object oriented Perl puts you into the big leagues. It's the first step into writing more powerful and complex Perl.

Perl hash key determined by array

I have an array and I am making a hash instance from it.
For instance, if array is:
#folders=(temp,usr,bin);
then i want to fill in hash:
$the_path{$folders[0]}{$folders[1]}{$folders[2]}="somevalue";
But if the array is only:
#folders=(bin);
then i want the path to be:
$the_path{$folders[0]}="somevalue";
The problem is I dont know beforehand how long the array is gonna be, and I would really like to avoid making x if statements for that solution scales terribly.
How do I do this?
First, that's not how you define an array in Perl. You probably want to say
my #folders = ( 'temp', 'usr', 'bin' );
There's an old trick for making nested hash keys from a list:
my %the_path;
my $tmp = \%the_path;
foreach my $item( #folders ) {
$tmp->{$item} = { };
$tmp = $tmp->{$item};
}
This will result in a structure like the following:
$VAR1 = {
'temp' => {
'usr' => {
'bin' => {}
}
}
};
If you want to replace the empty hashref at the bottom-most level with a string, you can keep track of a count variable inside the loop.