2
votes

System: Perl 5.26.1 on Ubuntu 18.04.

We upgraded our Ubuntu to 18.04 and Perl to 5.26 and now the experimental way I was using 2d hashes in Perl 5.18 is no longer supported. That's my fault for using something experimental.

My goal: I'm trying to use the new way to do hash of hashes, or a hash of 2 keys, or a 2d hash. I.e. a hash would have 2 keys, a department, and each department would have one or more employee ids. There can also be many departments in the hash as the first key. I'm reading data from the results of an SQL statement so I have to add hours to each employee id. Part of the problem is I read the department first, then I get the empid.

Problem: I'm getting an error in second loop to get the empid. Runtime error is: "Can't use string ("6") as a HASH ref while "strict refs" in use".

  • I've been doing research and I can't find a specific example for my case, the examples are different than what I'm doing, or the examples I try do not work and give me an error.
  • At least half the pages I've read have no date on them so I can't even guess at what version of perl they are for.
  • The perldocs on this topic are the same as other pages I've found on the internet.
  • I can't just use a plain SQL statement for this report because I have to do much more processing after I get the data for each employee.
  • I've tried several permutations of code with no luck. But I exclude those permutations here.
  • Most examples I've found on the internet do not work with Perl 5.26 for one reason or another.

Here is my code. I've tried several permutations from different sources to get this to work.

use strict;
use warnings;


use Data::Dumper;

####################
# Variables
my $i=0;
my $s='';
my $t='';
my $k='';
my $k2='';
my $empid='';
my $z='';
my @k=(); # Key list
my @k2=(); # Key list

my %hashs=(); # Scalar hash, works.
my %empees=();
my $pos=0;
my $val="Myval";
my $dept='';

####################
$s="Data::Dumper $Data::Dumper::VERSION";
print "$s\n";
print "\n";

$empees{'JSMITH'}=1.0; # WORKS
$empees{'RGREEN'}=2.0;
$empees{'KJONES'}=3.0;
$hashs{950}=%empees;

$empees{'WSMIT'}=1.5;
$empees{'AMCBE'}=2.5;
$empees{'SCHWAR'}=3.5;
$hashs{800}=%empees;

# Now print out values in 2d hash.
@k=keys(%hashs);
print "Keys and values are:\n";
foreach $dept (sort keys %hashs)
    {
    for $empid (sort keys %{$hashs{$dept}} ) # ERROR is here
        {
        $val=$hashs{$dept}{$empid};
        $t="$dept $empid $val";
        print "$t\n";
        } # foreach $empid
    } # foreach $dept
  • Can someone help me out here?
  • Is there another way to get this done than a hash of hashes?

Thank you for all your help! I really appreciate your time!

3
What were you doing before? The common way to do this has been mostly fixed since Perl 5.0.brian d foy
This code: $hashs{950}=%empees does not do what you think? It assigns a value equal to the number of keys in %empees.Håkon Hægland

3 Answers

2
votes

This snippet works for me

$hashs{950}=\%empees;

$empees{'WSMIT'}=1.5;
$empees{'AMCBE'}=2.5;
$empees{'SCHWAR'}=3.5;
$hashs{800}=\%empees;

# Now print out values in 2d hash.
@k=keys(%hashs);
print "Keys and values are:\n";
for my $dept (sort keys %hashs) {
    for my $empid (sort keys %{$hashs{$dept}}) {
        print "$empid\n";
        $val=$hashs{$dept}{$empid};
        $t="$dept $empid $val";
        print "$t\n";
    } # foreach $empid
} # foreach $dept
0
votes

Value of a hash is always a scalar.

So, instead of assigning a hash

$hashs{950}=%empees;

assign a hash reference:

$hashs{950} = { %empees };

This only works if the %empees is one-level hash, otherwise you need to make a deep copy, e.g.

use Storable qw{ dclone };
# ...
$hashs{950} = dclone(\%empees);

and similarly for 800 (you probably need to empty %empees, otherwise you'd assing the values already assigned to 950, too).

Or, assign it directly:

$hashs{950} = { JSMITH => 1.0,
                RGREEN => 2.0,
                KJONES => 3.0 };
0
votes

OP's question does not cover the problem in full extent.

Please see the following piece of code which demonstrates how hash departments can be filled up with data and how these data can be retrieved for use latter.

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my $debug = 1;

my %departments;
my $department;

while( <DATA> ) {
    $department = $1 if /^(\d+)/;
    $departments{$department}{$1}=$2 if /^\s+(\w+):([\d\.]+)/;
}

say Dumper(\%departments) if $debug;

foreach $department (sort keys %departments) {
    say $department;
    foreach my $employee (sort keys %{$departments{$department}}) {
        say "\t $employee $departments{$department}{$employee}";
    }
}

__DATA__
950
    JSMITH:1.0
    RGREEN:2.0
    KJONES:3.0
800
    WSMIT:1.5
    AMCBE:2.5
    SCHWAR:3.5

Output

$VAR1 = {
          '950' => {
                     'JSMITH' => '1.0',
                     'KJONES' => '3.0',
                     'RGREEN' => '2.0'
                   },
          '800' => {
                     'WSMIT' => '1.5',
                     'SCHWAR' => '3.5',
                     'AMCBE' => '2.5'
                   }
        };

800
         AMCBE 2.5
         SCHWAR 3.5
         WSMIT 1.5
950
         JSMITH 1.0
         KJONES 3.0
         RGREEN 2.0