how to copy(insert) hash reference to another hash reference in perl? - 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->{ $_ } ;
}

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()->%*

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

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.

Folding Array of hashes to HoH

I have $maps as AoH which I wish to make a $new_map to be a HoH based on a member of the enclosing hashes.
I currently have:
map { $new_map->{$_->{TYPE}} = $_; delete $_->{TYPE} } #$maps;
This does the job..
I wonder if there's a better/simpler/cleaner way to get the intent. Perhaps, by getting the return value from map?
$new_map = map { ... } #$maps;
Thanks
Your original solution is a misuse of map as it doesn't use the list that the operator returns. for is the correct tool here, and I think it reads much better that way too, especially if you use the fact that delete returns the value of the element it has removed
$new_map->{ delete $_->{TYPE} } = $_ for #$maps;
Or you could translate the array using map properly, as here
my %new_map = map { delete $_->{TYPE} => $_ } #$maps;
The choice is your own
Using map in void context obfuscates the intent, and altering original #$maps may not be a good idea (map with side effects?), thus
my $new_map = {
map { my %h = %$_; delete $h{TYPE} => \%h } #$maps
};