8

Based on my current understanding of hashes in Perl, I would expect this code to print "hello world." It instead prints nothing.

%a=();

%b=();
$b{str} = "hello";  
$a{1}=%b;

$b=();
$b{str} = "world";
$a{2}=%b;

print "$a{1}{str}  $a{2}{str}"; 

I assume that a hash is just like an array, so why can't I make a hash contain another?

Axeman
  • 29,660
  • 2
  • 47
  • 102
Mike
  • 58,961
  • 76
  • 175
  • 221
  • 3
    The short and sweet of why this doesn't work is that because, basically, a hash or an array can only contain scalar values, and hashes are not scalars. However, hash references are. :) The answers below have some good links about why this is. – Robert P May 25 '10 at 21:34

6 Answers6

6
  1. You should always use "use strict;" in your program.

  2. Use references and anonymous hashes.

use strict;use warnings;
my %a;

my %b;
$b{str} = "hello";  
$a{1}={%b};

%b=();
$b{str} = "world";
$a{2}={%b};

print "$a{1}{str}  $a{2}{str}";

{%b} creates reference to copy of hash %b. You need copy here because you empty it later.

daotoad
  • 26,689
  • 7
  • 59
  • 100
Alexandr Ciornii
  • 7,346
  • 1
  • 25
  • 29
6

Hashes of hashes are tricky to get right the first time. In this case

$a{1} = { %b };
...
$a{2} = { %b };

will get you where you want to go.

See perldoc perllol for the gory details about two-dimensional data structures in Perl.

mob
  • 117,087
  • 18
  • 149
  • 283
  • 1
    `perldoc perllol` is all arrays, all the time, I think. `perldoc perldsc` moves into more complicated scenarios (and includes hashes). I also highly recommend `perldoc perlreftut` for anyone new to references in Perl. – Telemachus May 26 '10 at 00:52
4

Short answer: hash keys can only be associated with a scalar, not a hash. To do what you want, you need to use references.

Rather than re-hash (heh) how to create multi-level data structures, I suggest you read perlreftut. perlref is more complete, but it's a bit overwhelming at first.

David Wall
  • 51
  • 2
  • Thanks! I forgot about perldsc. I had to look at perlref to remember that perlreftut existed. :-) I think I should re-familiarize myself with the docs. – David Wall May 25 '10 at 21:55
  • @davidwall Reviewing the docs is always good, but the tutorials section in particular is easy to forget but not to be missed. I especially remember liking `perlre`, `perlreftut`, `perldsc`, `perlopentut`, `perltoot` (maybe this one is out of date or out of touch with current best practices about OO in Perl - not sure) and `perlstyle`. – Telemachus May 26 '10 at 11:31
2

Mike, Alexandr's is the right answer.

Also a tip. If you are just learning hashes perl has a module called Data::Dumper that can pretty-print your data structures for you, which is really handy when you'd like to check what values your data structures have.

use Data::Dumper;
print Dumper(\%a); 

when you print this it shows

$VAR1 = {
          '1' => {
                   'str' => 'hello'
                 },
          '2' => {
                   'str' => 'world'
                 }
        };
dalton
  • 3,656
  • 1
  • 25
  • 25
1

Perl likes to flatten your data structures. That's often a good thing...for example, (@options, "another option", "yet another") ends up as one list.

If you really mean to have one structure inside another, the inner structure needs to be a reference. Like so:

%a{1} = { %b };  

The braces denote a hash, which you're filling with values from %b, and getting back as a reference rather than a straight hash.

You could also say

$a{1} = \%b;   

but that makes changes to %b change $a{1} as well.

cHao
  • 84,970
  • 20
  • 145
  • 172
0

I needed to create 1000 employees records for testing a T&A system. The employee records were stored in a hash where the key was the employee's identity number, and the value was a hash of their name, date of birth, and date of hire etc. Here's how...

# declare an empty hash
%employees = ();

# add each employee to the hash
$employees{$identity} = {gender=>$gender, forename=>$forename, surname=>$surname, dob=>$dob, doh=>$doh};

# dump the hash as CSV
foreach $identity ( keys %employees ){
    print "$identity,$employees{$identity}{forename},$employees{$identity}{surname}\n";
}
Clarius
  • 1,183
  • 10
  • 10