Reputation: 655
I'm including code that I've verified illustrates my problem. My objective is to take a hash returned from a database query simulated here as %r (which contains the numerical indexing) and use the value of the "name" field as the key in the receiving hash. (This hash will contain results from multiple queries.)
I feel it's likely I'm doing something stupid with references and dereferencing. I'd really like to try to grasp the concepts at work here. Thanks!
#!/usr/bin/perl -T
use strict;
use warnings;
my %hash;
my %r = (
'1' => {
name => 'Harry',
desc => 'Prince',
},
);
MergeHash(\%hash,\%r);
foreach my $name (keys %hash) {
print "name = $name, desc = $hash{$name}{desc}\n"; # EDIT: REVISED
}
exit;
sub MergeHash {
my $h = shift; # reference to receiving hash
my $r = shift; # reference to giving hash
foreach my $i (keys %{$r}) {
$$h{name} = $$r{$i}{name} || 'Unknown'; # okay
$$h{name}{desc} = $$r{$i}{desc} || 'Unknown'; # Can't use string ("Harry") as a HASH ref while "strict refs" in use at ./test.pl line 25.
}
}
Edit: As requested, the output (also indicated in the code above as comments):
Can't use string ("Harry") as a HASH ref while "strict refs" in use at ./test.pl line 25.
Edit #2: Revised print line (in code above) to make it clear the structure desired in %hash.
Upvotes: 1
Views: 335
Reputation: 385657
$$h{name}{desc}
is more clearly written as
$h->{name}{desc}
which is short for
$h->{name}->{desc}
As this makes clearer, the value of $h->{name}
is being used as a hash reference (just like the value of $h
was a moment before), but it contains a string.
All you need to do replace name
with the actual name, because that's what you want the key to be.
for my $i (keys(%$r)) {
$h->{ $r->{$i}{name} }{desc} = $r->{$i}{desc};
}
We don't actually need $i
, so let's simplify.
for my $row (values(%$r)) {
$h->{ $row->{name} }{desc} = $row->{desc};
}
The problem with the above is that it doesn't lend itself to more than one field per row. The following deals with that better:
for my $row (values(%$r)) {
$h->{ $row->{name} } = {
desc => $row->{desc},
# ...
};
}
Buy why build a new hash when could just the one we already have?
for my $row (values(%$r)) {
$h->{ $row->{name} } = $row;
}
Upvotes: 3
Reputation: 3190
If you want to overwrite the receiving hash's member with all of the data from the giving hash, then the following code will work.
sub MergeHash {
my $h = shift; # reference to receiving hash
my $r = shift; # reference to giving hash
while (my ($k, $v) = each %$r) {
$h->{$k} = $v;
}
}
The problem is that $$h assumes $h is a reference to a SCALAR and it is not, it is a reference to a HASH, you usually see people either dereference the hash while referring to one its members as I have done above. The other way is to declare a copy of the hash like this, but which would not work for your use case:
my %copyofh = %$h;
Upvotes: -2