Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical

Hashes... Light switch isn't coming on

by agentorange (Sexton)
on Dec 12, 2012 at 16:51 UTC ( #1008529=perlquestion: print w/replies, xml ) Need Help??
agentorange has asked for the wisdom of the Perl Monks concerning the following question:

Hi guys hoping you can help

For whatever reason that switch inside my head that turns on the light to say I understand hashes just will not flick on! It's possible that I'm running before I can walk as I've forced my way through understanding and retrieving data from complex hashes, and written good few bits of code in the last few months, but I just cannot get it into my head how to build those hashes unless I explicitly hard code them.

Can you assist with a sample piece of code that may finally enable me to crack it?

#!/bin/perl # use Data::Dumper; my @primary = qw( foo bar ); my @secondary = qw( p0 p1 p2 ); my @tertiary = qw( itema itemb ); my %HoH; foreach $primary( @primary ) { print "\t$primary\n"; foreach $secondary( @secondary ) { print "\t\t$secondary\n"; foreach $tertiary( @tertiary ) { print "\t\t\t$tertiary\n"; %HoH = ( $primary => $secondary => $tertiary); #%HoH = {$primary}{$secondary} => $tertiary); } } } print Dumper \%HoH;

The basic debug output from the print should describe the hash I'm trying to create. That is a key of "primary", containing a key of "secondary" elements with elements of "tertiary".

foo => p0 => itema itemb p1 => itema itemb p2 => itema itemb bar => p0 => itema itemb p1 => itema itemb p2 => itema itemb

But what I actually get is

$VAR1 = { 'bar' => 'p2', 'itemb' => undef };

Replies are listed 'Best First'.
Re: Hashes... Light switch isn't coming on
by LanX (Archbishop) on Dec 12, 2012 at 16:54 UTC

    %HoH = {$primary}{$secondary} => $tertiary);


     $HoH{$primary}{$secondary} = $tertiary;


    But what I actually get is

    $VAR1 = { 'bar' => 'p2', 'itemb' => undef };

    sure, you're redefining the complete Hash in the deepest loop, and these are the three values of the last iteration. (plus undef because hashes are always even)

    Cheers Rolf

    ) which has a syntax error at ), please only post code you really tried.

Re: Hashes... Light switch isn't coming on
by jethro (Monsignor) on Dec 12, 2012 at 17:35 UTC

    To supplement the answer by LanX, there are two ways to fill an array or hash: Either you initialize it with all values at once or you insert values one at a time. You seem to mix up these two methods. Here is how both work for arrays and hashes:

    # initialize an array at once with 3 scalars, two array pointers and o +ne hash pointer: @array= (1,2,3,\@anotherarray,[1,2],{1=>15}); # initialize a hash at once with three keys. Data is one scalar, one h +ash pointer and one array pointer %hash= (1=>15, 7=>{5=>4}, 2=>[1,2,3]);

    As you can see initializing at once is not complicated, you just have to remember that hash and array are both initialized with an array constant that uses (), but constant array pointers are specified with [] and constant hash pointers are specified with {}. Also any old values are completely erased so this doesn't work when you want to add values one by one

    Inserting values one at a time (mostly in a loop) on the other hand uses the same "interface" as any normal array or hash operation, so:

    foreach ... { $array[$i][$j]= $value; # or push( @{$array[$i]}, $value); $hash{$i}{$j}= $value; }

    Since only one value inside the array or hash is changed or added and all other values remain, this works very well in a loop

      Note that a pointer is not the same as a reference. A pointer points to a memory location where a value might be stored, whereas a reference —at least in Perl parlance— points to a value.

        Wikipedia says

        While "pointer" has been used to refer to references in general, it more properly applies to data structures whose interface explicitly allows the pointer to be manipulated (arithmetically via pointer arithmetic) as a memory address...

        So, while you are correct that there is a subtle difference between those words, in a language where low-level pointers don't exist it should be obvious that pointer is an alias for reference

        But thanks for the info, I wasn't really aware of the distinction

Re: Hashes... Light switch isn't coming on
by muba (Priest) on Dec 12, 2012 at 18:11 UTC

    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;


    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;


    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;


    Edit: fixed two typos and one slightly misleading comment

Re: Hashes... Light switch isn't coming on
by blue_cowdawg (Monsignor) on Dec 12, 2012 at 18:10 UTC

    foreach $primary( @primary ) { print "\t$primary\n"; foreach $secondary( @secondary ) { print "\t\t$secondary\n"; foreach $tertiary( @tertiary ) { print "\t\t\t$tertiary\n"; #%HoH = ( $primary => $secondary => $tertiary); #%HoH = {$primary}{$secondary} => $tertiary); # $HoH{$primary} -> {$secondary} = [] unless ref($HoH{$primary} -> {$secondary}) eq 'ARRA +Y'; push @{$HoH{$primary} -> {$secondary}},$tertiary; } } }
    Was that what you were looking for?

    Peter L. Berghold -- Unix Professional
    Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
Re: Hashes... Light switch isn't coming on
by agentorange (Sexton) on Dec 13, 2012 at 17:04 UTC

    Many thanks all these replies are most useful! blue_cowdawg yes that is exactly what I'm after and I think half my problem is not fully understanding the hash construct and its terminology.

    So blue_cowdawg please don't be offended :o) but whilst I've tried your code and it does exactly what I want I'm not going to use it. Instead I'm going to spend a bit of time working through that excellent post from muba until that switch flicks and I can code what I want! I'll use yours if I fail ;o)

    I suspect it's actually more straight forward than I think and in addition my old C programming days from 15+ years are possibly confusing my understanding. Perl feels like C to me.
    Interestingly I now find ksh, although still highly useful, ****** awful to write and prefer the style of Perl.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1008529]
Approved by muba
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (8)
As of 2018-11-14 20:26 GMT
Find Nodes?
    Voting Booth?
    My code is most likely broken because:

    Results (177 votes). Check out past polls.