Search code examples
perlreferencedereference

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.


Solution

  • $$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;
    }