Perl return list of array refs of unknown length - perl

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 ];

Related

How to compare two perl object values?

I need to compare two Perl object values, one is from a variable and another one is from an array value
ImmediateParent and data contain path directive values(C:\Users\Public\Documents)
while (length(basename(dirname(($immediateParent)))) > 1)
{
$immediateParent = (dirname(($immediateParent)));
my ($dictionaryitem) = $';
my $boolean =0;
foreach $dictionaryitem (#data)
{
if ($immediateParent eq $dictionaryitem->[0])
{
$boolean = 1;
last;
}
}
if ($boolean)
{
last;
}
}
I attempted to compare the values of two paths directories, but the condition always returned true, so it was ineffective. Would you kindly advise me on how to compare two path values?
If Data::Dumper shows
$immediateParent = 'C:\\Users\\Public';
$dictionaryitem = 'C:\\Users\\Public';
then you should compare them directly without array dereference, i.e. remove the ->[0]:
if ($immediateParent eq $dictionaryitem)
The fact that Perl let you dereference a string is weird. Are you not using strict?

Iterating the results returned from fetchall_arrayref

I have a sql wherein I am fetching few records, sorted by full name.
My requirement is to extract chunks of similar names and then do some operation on it.
Say, the sql returns some records containing names like [name1,name1,name2,name3,name3]
I need to split them to [name1,name1] , [name2] , [name3,name3]
I am able to do it, but I am not happy with my implementation as I have to call doSomethingWithNames()twice.
while (my $paRecs = $pStatementHandle->fetchall_arrayref({}, 3)){
foreach my $phRec(#{$paRecs}){
my $pCurrentName = $phRec->{'FULL_NAME'};
if ((scalar(#aParentRecords) eq 0) or ($aParentRecords[-1] eq $pCurrentName)){
push(#aParentRecords, $pCurrentName);
} else {
doSomethingWithNames(\#aParentRecords);
#aParentRecords= ();
push(#aParentRecords, $pCurrentName);
}
}
};
doSomethingWithNames(\#aParentRecords); # This should be inside while loop
I believe am running into this issue because while doesn't go into the loop for
the last iteration as fetch* returns undef.
Sounds basic PERL stuff, but tried many loop constructs with no luck.
Any pointer will be a great help
The trick is to postpone existing the loop by converting it into an infinite loop. This requires checking the loop-terminating condition (!$rows) twice, though.
my $rows = [];
my $prev_name = '';
my #group;
while (1) {
$rows = $sth->fetchall_arrayref({}, 3) if !#$rows;
if (!$rows || $rows->[0]->{FULL_NAME} ne $prev_name)
if (#group) {
do_something(\#group);
#group = ();
}
last if !$rows;
}
$prev_name = $rows->[0]->{FULL_NAME};
push #group, shift(#$rows);
}

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.

Convert Hash To Array in Perl Catalyst

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?

how to copy(insert) hash reference to another hash reference in perl?

recently started doing perl. I'm reading old code and trying to rewrite some stuff. I've got a question here about hash references.
#declar anon hash ref that will be returned
my $store_hash = {};
foreach my $item (#list)
{
#this will iterate based on list
my $ret_hash = ops_getval($item, $user)
#do some magic here to map $ret_hash into $store_hash
}
ops_getval is a function that returns a type of ref hash. I want to insert those values into $store_hash. How should I approach this? Can I directly do
$store_hash = ops_getval($var1,$var2)
Much appreciated!
I think the standard way to do this is:
#$store_hash{ keys %$ret_hash } = values %$ret_hash;
This merges all of the hashes returned by all of the calls to ops_getval into $store_hash.
An alternate approach that might be clearer to the eye, possibly at the cost of a lot of redundant data copying:
%$store_hash = (%$store_hash, %$ret_hash);
You would do something like:
$store_hash->{$item} = $ret_hash
In general:
$hashref->{$key} = $value
See here for more: http://perldoc.perl.org/perlref.html#Using-References
To be clear, you can use a loop and get this done.
foreach ( keys%{ $ret_hash } ){
$store_hash->{ $_ } = $ret_hash->{ $_ } ;
}