Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Re: Hashes... Light switch isn't coming on

by muba (Priest)
on Dec 12, 2012 at 18:11 UTC ( [id://1008548]=note: print w/replies, xml ) Need Help??


in reply to Hashes... Light switch isn't coming on

Let's tackle this one step at a time. That is, let's deal with plain, flat hashes before we look at complex data structures. Say you want a hash with names of your friends as keys, and their telephone numbers as values.

# Snippet 1 - a stepping stone use strict; use warnings; use Data::Dumper; # The phone book in an ordinary format, # as it might be read from a text file for example my $numbers = "Alice: 555 1234\n" . "Bob: 555 9876\n" . "Charlie: 555 2580"; # The hash. Still empty. my %phonebook = (); # Split $numbers on newlines, then iterate over the returned elements # Then get the name and the number from each element and store them in + the hash. for my $line ( split(/\n/, $numbers) ) { my ($name,$number) = split(/:\s+/, $line); $phonebook{$name} = $number; } # Bob's a cool guy. I should call him. What's his number again? print "Bob's digits are $phonebook{Bob}\n\n"; # Heck, Alice, Bob, Charlie -- they're all pretty cool! print Dumper \%phonebook;

Output:

Bob's digits are 555 9876 $VAR1 = { 'Bob' => '555 9876', 'Alice' => '555 1234', 'Charlie' => '555 2580' };

There's a number of things to learn from this snippet:

  1. When you're talking about a hash as a whole (as opposed to a single element in it), you use the % sigil, as in %phonebook = (); and print Dumper \%phonebook;
  2. If you're storing a key/value pair in a hash, or retrieving the value associated with a given key, you use the $ sigil, as in $phonebook{$name} = $number; and print "Bob's digits are $phonebook{Bob}\n\n";
  3. To specify the key you want to use, either for storing or fetching a value, you use the curly braces { and } around the key

Moving on. Values in a hash can hold any scalar - strings, numbers, references... Complex data structures in Perl all revolve around references. In your case, these will be references to hashes. There are three ways to create them. Let's add a feature to our phone book that allows us to store different kind of phone numbers (office, home, mobile, ...) for our friends.

# Snippet 2a - another stepping stone use strict; use warnings; use Data::Dumper; # Create a normal hash. my %alice = ( home => "555 1234", work => "555 9876", mobile => "555 2580", ); # And another one. my %bob = ( home => "555 2345", work => "555 8765", mobile => "555 1357", ); my %phonebook = (); $phonebook{Alice} = \%alice; # Method 1: Store a direct reference t +o the %alice hash $phonebook{Bob} = { %bob }; # Method 2: Create an anonymous hash r +ef, with the contents of %bob $phonebook{Charlie} = { # Method 3: Create and store an anonym +ous hash ref directly home => "555 3456", work => "555 7654", mobile => "555 2468" }; # Alice's a cool lady. I want to call her at her home number! print "Alice's home number is $phonebook{Alice}->{home}\n"; # She's not there. Maybe she's with Charlie, let's call his cell phone +. print "Charlie's mobile number is $phonebook{Charlie}->{mobile}\n\n"; # Gimme all their digits! print Dumper \%phonebook;

Output:

Alice's home number is 555 1234 Charlie's mobile number is 555 2468 $VAR1 = { 'Bob' => { 'work' => '555 8765', 'home' => '555 2345', 'mobile' => '555 1357' }, 'Alice' => { 'work' => '555 9876', 'mobile' => '555 2580', 'home' => '555 1234' }, 'Charlie' => { 'work' => '555 7654', 'mobile' => '555 2468', 'home' => '555 3456' } };

To see the difference between method 1 and method 2, append the following lines to the above snippet:

# Snippet 2b - append to snippet 2a for (1..3) { printf "Alice and Bob: %s and %s\n", \%alice, {%bob} }
Alice and Bob: HASH(0x328358) and HASH(0x380138) Alice and Bob: HASH(0x328358) and HASH(0x3801e0) Alice and Bob: HASH(0x328358) and HASH(0x380168)

You'll see that referring to an existing hash (as with \%alice) will constantly give the same reference, but creating a new anonymous hash (as with {%bob}) will give you a new value every time. Why is this relevant? Add the following lines to the code:

# Snippet 2c -- append after 2b delete $alice{home}; # Delete Alice's home number **from he +r original hash** delete $bob{home}; # Delete Bob's home number **from his +orignal hash** too delete $phonebook{Charlie}; # Let's not clutter the output print Dumper \%phonebook;
$VAR1 = { 'Bob' => { 'work' => '555 8765', 'home' => '555 2345', 'mobile' => '555 1357' }, 'Alice' => { 'work' => '555 9876', 'mobile' => '555 2580' } };

You'll see that, even though we've deleted a number from Alice's hash, and one from Bob's hash, Bob still has his home number in %phonebook whereas Alice doesn't. Why is that? It's because $phonebook{Alice} is a reference to %alice, but $phonebook{Bob} holds an anonymous hashref. The difference is subtle, but worth pointing out.

Does this mean one method is better than the other? Oh, not at all - it all depends on what you need. But I digress.

This should get you on your way. However, what you want precisely is a little unclear to me. The value associated with <$secondary>, what do you want this to be? Your desired output seems to suggest an array reference, but I can't tell from your code. Assuming that's what you want, though, how about this:

# Snippet 3 - your answer? use strict; use warnings; use Data::Dumper; my @primary = qw(foo-Alice bar-Bob); my @secondary = qw(p0-home p1-work p2-mobile); my @tertiary = qw(itemA-555abcd itemB-555zxyw); my %phonebook = (); for my $primary_element (@primary) { for my $secondary_element (@secondary) { # Similar to method 1 from snippet 2a: # a reference directly to @tertiary $phonebook{$primary_element}->{$secondary_element} = \@ter +tiary; # -- or -- # Similar to method 2 from snippet 2a: # a new anonymous array reference, containing the elements + from @tertiary $phonebook{$primary_element}->{$secondary_element} = [ @te +rtiary ]; } } print Dumper \%phonebook;

Output:

$VAR1 = { 'bar-Bob' => { 'p1-work' => [ 'itemA-555abcd', 'itemB-555zxyw' ], 'p0-home' => [ 'itemA-555abcd', 'itemB-555zxyw' ], 'p2-mobile' => [ 'itemA-555abcd', 'itemB-555zxyw' ] }, 'foo-Alice' => { 'p1-work' => [ 'itemA-555abcd', 'itemB-555zxyw' ], 'p0-home' => [ 'itemA-555abcd', 'itemB-555zxyw' ], 'p2-mobile' => [ 'itemA-555abcd', 'itemB-555zxyw' ] } };

Edit: fixed two typos and one slightly misleading comment

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1008548]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (2)
As of 2024-04-19 19:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found