user3802478
user3802478

Reputation: 143

Getting HASH ref error in Perl

I'm trying to understand why I get a HASH ref error in one situation and not another. Error: Can't use string ("1") as a HASH ref while "strict refs" in use at ./a.pl line 351. Error line indicated by <--

Problem code:

my $count = 0;
my %sort_total;

foreach my $host (keys %ips_per_host) {
  foreach my $ip (keys %{$ips_per_host{$host}}) {
    $sort_total{$count} = $ips_per_host{$host}{$ip};
    $sort_total{$count}{'host'} = $host;  <--
    $sort_total{$count}{'ip'} = $ip;
    $count++;
  }
}
foreach $count (sort {$sort_total{$a} cmp $sort_total{$b} }keys %sort_total){
  [...]
}

However, if I create another hash and use that instead, no error.

my $count = 0;
my %sort_total;
my %sort_hosts;

foreach my $host (keys %ips_per_host) {
  foreach my $ip (keys %{$ips_per_host{$host}}) {
    $sort_total{$count} = $ips_per_host{$host}{$ip};
    $sort_hosts{$count}{'host'} = $host;
    $sort_hosts{$count}{'ip'} = $ip;
    $count++;
  }
}

foreach $count (sort {$sort_total{$a} cmp $sort_total{$b} }keys %sort_total){
  [...]
}

Can anyone help me understand why this occurs? I've run into it on several occasions, and I'd really like to wrap my head around it.

Upvotes: 1

Views: 185

Answers (1)

friedo
friedo

Reputation: 66957

The reason you're getting this error is because this expression:

$ips_per_host{$host}{$ip}

returns the number 1. That's a perfectly fine thing to store in $sort_total{$count}, which you do.

The problem is that you then try to put stuff in $sort_total{$count}{'host'}.

When you use a nested data structure, Perl will attempt to "autovivify" (bring into existence) the layers of the structure necessary for the nested thing to exist. So Perl checks to see if $sort_total{$count} exists, and it does. Then it checks if that value is a reference to another hash, since your expression has a two-level hash. But it isn't; it's already set to the value 1. If Perl were to autovivify a hash reference there, it would erase what you've already stored, which is probably not what you want. So Perl exits with an error, assuming that you would rather correct this instead of overwriting your data.

So there are a few options.

  1. If $ips_per_host{$host}{$ip} is supposed to actually be returning a hashref and not a number, then that's a bug, so fix it.

  2. If $ips_per_host{$host}{$ip} really is supposed to be a number, then assign it to a key in the hash you want to use, e.g. $sort_total{$count}{per_host} = $ips_per_host{$host}{$ip};

  3. Use two separate hashes as you're doing now.

Upvotes: 7

Related Questions