Timothy B.
Timothy B.

Reputation: 655

Strict refs error when working with hash reference

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

Answers (2)

ikegami
ikegami

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

Jeff Holt
Jeff Holt

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

Related Questions