push (#{$processor{$-[0]}}, $metadata[$_]{"formatters"});
foreach my $key (keys #{$metadata[$_]{"formatters"}}) {
$metadata[$_]{"formatters"}[$key]{"scope"} = "end";
}
push (#{$processor{$+[0]}}, $metadata[$_]{"formatters"});
I push $metadata[$_]{"formatters"} into #{$processor{$-[0]}}.
Then i need change something and push it again, but these changes are reflected too where i push first. Seems like i am passing this $metadata[$_]{"formatters"} by reference and not by value, but i can`t find, how to pass it by value.
Since $metadata[$_]{'formatters'} is itself a complex datastructure, it doesn't have a "value" per se. You can push a copy of it, though.
To just make a shallow copy (probably not what you want, since the new array will still have references to all the same hashes):
$orig = $metadata[$_]{'formatters'};
$copy = [ #$orig ];
To copy each referred-to hash also:
$orig = $metadata[$_]{'formatters'};
$copy = [ map +{%$_}, #$orig ];
Or to just copy any arbitrary datastructure:
$copy = Storable::dclone($orig);
(There are various Clone modules on CPAN that also do this.)
I am very worried about your design. A structure like
$metadata[$_]{formatters}[$key]{scope}
is complex, and it seems you have multiple copies of very similar data.
However, the problem is that $metadata[$_]{formatters} is a reference to an array of hashes. Wherever you put that reference it will refer to the same data, so modifying it will be reflected in any reference to it.
You need to copy your array of hashes to an identical data structure before modifying it, and you can do that using this code. I have a suspicion that the array is a sparse one (i.e. that not all of its elements contain data) and have coded accordingly. If I am wrong, and every element is a hash reference, then you can simplify this to my $copy = [ map { {%$_} } #$formatters ].
my $formatters = $metadata[$_]{formatters};
{
my $copy = [ map $_ ? {%$_} : undef, #$formatters ];
push #{$processor{$-[0]}}, $copy;
for my $key (0 .. $#$copy) {
$copy->[$key]{scope} = 'end';
}
}
{
my $copy = [ map $_ ? {%$_} : undef, #$formatters ];
push #{$processor{$+[0]}}, $copy;
}
This code won't even compile.
You are calling keys on #{$metadata[$_]{"formatters"}}
which is not a hash, it's an Array.
The question is ill-formed.
This code is incredibly convoluted and hard to understand.
I strongly recommend using the -> notation when nesting
structures like this to make it clear that you are using references
to substructures.
To deep-copy a structure so that you don't end up getting
references to subcomponents, I would suggest cloning. e.g.:
use Clone qw(clone);
push (#{$processor{$-[0]}}, clone($metadata[$_]{"formatters"}));
Related
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 ];
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.
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
};
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->{ $_ } ;
}
I have a recursive function that uses ref to walk down a data structure recursively. If ref returns an empty string, then a callback is called. My problem is that I need to store a hash-ref in my data structure that will be treated as a scalar by my function.
In essence, what I need is to do something like this;
my %hash = fillHash();
$hash{'abc'}{'def'} = \%hash;
I was wondering if there was some way of storing \%hash in such a way that it would be treated as a scalar and not a reference. possibly like/
$hash{'abc'}{'def'} = scalar \%hash;
I am simply looking a way to add a link to the parent node of my data structure, yet still being able to recursively walk it.
Thanks
You could use what I did to differentiate values from structure, I call them "indirect arrays" (in my case).
In your case it would look like this:
$hash{'abc'}{'def'} = scalar \\%hash;
ref( $hash{'abc'}{'def'} ) is 'REF', in case you're wondering. And you can then decide that you need to dereference it twice:
$hash_ref = ${ $hash{'abc'}{'def'} };
I think #MarkCanlas has the right solution in his suggestion to consider a different way to structure your data. Barring that, you could take a double reference.
my $scalar_ref = \%hash;
$other_hash{abc}{def} = \$scalar_ref;
Now, when you check the ref of that you'll get back "REF" and can do something different.
First of all, you should realize that all references are scalars by definition.
What you want is something that works as a reference everywhere but inside your walking function. That's not really achievable by any kind of magic reference. The walking function is where the logic is going to have to be added. You can make it keep track of things it has already seen:
my %h = (
foo => "bar",
inner => {
foo => 42
}
);
$h{inner}{parent} = \%h;
sub walk
{
my ($h, $cb, $path, $seen) = #_;
$path //= "";
$seen //= {};
$seen->{$h}=1;
while(my ($k, $v) = each %$h) {
if(ref($v)) {
walk($v, $cb, "$path/$k", $seen) if !$seen->{$v};
} else {
$cb->($k, $v, "$path/$k");
}
}
}
walk(\%h, sub {
my ($key, $value, $path) = #_;
print "walker found $key ($path) => $value\n";
});
Or you could just make it recognize certain keys like parent as special and skip them.