Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

(another) HoH question

by zuma53 (Beadle)
on Jun 23, 2012 at 17:30 UTC ( [id://977990]=perlquestion: print w/replies, xml ) Need Help??

zuma53 has asked for the wisdom of the Perl Monks concerning the following question:

I've written this:
$hash{tree} = 4; $hash{tree}->{apple} = 6; $hash{tree}->{apple}->{red} = 9; $hash{tree}->{apple}->{fuji} = 4; $hash{bird} = 4; $hash{bird}->{robin} = 4; $hash{snail} = 4; foreach $key (keys %hash) { print "$key ".$hash->{$key}."\n"; foreach $key2 (keys %{$hash->{$key}}) { print "$key $key2 ".$hash->{$key}->{$key2}."\n"; foreach $key2 (keys %{$hash->{$key}->{$key2}}) { print "$key $key2 $key3 ".$hash->{$key}->{$key2}->{$ +key3}."\n"; } } }
But when I run it, I only get the first-level keys (but not the associated first-level value).

This, in theory, should reprint my inputs without the '$hash' and the braces (but not necessarily in the order entered).

Is this not an appropriate way to use a hash? And how do I fix what's wrong?

Thanks!

Replies are listed 'Best First'.
Re: (another) HoH question
by muba (Priest) on Jun 23, 2012 at 18:27 UTC

    Because you can assign only one scalar value to a hash slot. So you can say $hash{tree} = 4; or you can say $hash{tree} = {apple => 6}; 1, but not both at the same time.

    1: This which would assign a scalar to $hash{tree}, that scalar being a reference to the anonymous hash {apple=>6}. In a way this boils down to the same as $hash->{tree}->{apple} $hash{tree}->{apple} (Update: typo kindly pointed out by AnomalousMonk), see for yourself:

    use strict; use warnings; use Data::Dump 'pp'; my %hash; $hash{tree}->{apple} = 6; pp \%hash; $hash{tree} = {apple => 6}; pp \%hash; __END__ { tree => { apple => 6 } } { tree => { apple => 6 } }

    If you really need a tree structure, perhaps a hash of arrays would be better suited. It'd get something like this:

    use strict; use warnings; use Data::Dump 'pp'; my %hash; $hash{tree} = [4]; push @{$hash{tree}}, {apple => [6]}; pp \%hash; my $apple_tree = $hash{tree}->[1]->{apple}; push @$apple_tree, {red => 9, fuju => 4}; pp \%hash; __END__ { tree => [4, { apple => [6] }] } { tree => [4, { apple => [6, { fuju => 4, red => 9 }] }] }

    Or, if you really want your own code to walk over this tree:

    for my $key (keys %hash) { my ($root_value, $subtree) = @{$hash{$key}}; print "$key $root_value\n"; for my $subtree_key (keys %$subtree) { my ($subtree_root_value, $subtree_subtree) = @{$subtree->{$sub +tree_key}}; print "$key $subtree_key $subtree_root_value\n"; for my $bottom_level_key (keys %$subtree_subtree) { print "$key $subtree_key $bottom_level_key ", $subtree_ +subtree->{$bottom_level_key}, "\n"; } } } __END__ tree 4 tree apple 6 tree apple fuju 4 tree apple red 9
    But as you can see that becomes pretty messy rather quickly, codewise. Perhaps you should reconsider the data structure you want to use?

      Thanks for your answers!

      I had a vague sense that something was blocking either the level-1 assignments or level-3 assignments. Data::Dumper certainly cleared that up (I had never used this before, and this will become a favorite.)

      What I'm trying to do is use a hash to do a hash to merge two sets:

      Set 1: I have a text file establishing the universe of accepted values:
      abc 123 abc 456 abc 789 xyz 456 Hash: 123->abc 456->abc 456->xyz 789->abc
      Set 2: A set of values pointing to the hash. There may be multiple recs pointing to a hash rec (that's OK, as I just need to know of the existence of one). There maybe hash records that no one uses.
      xxx ==> 456->abc xxx ==> 789->abc yyy ==> 456->abc yyy ==> 456->abc (a dup) zzz ==> 123->abc Resulting hash 123->abc->zzz 456->abc->xxx 456->abc->yyy 456->xyz 789->abc->xxx
      I could just hash the second set, but I won't get the non-use ones like 456->xyz.

      I've tried building the first hash and using arrays for the latter hash, but it runs rather slowly from all the repeated scans.

      Suggestions appreciated.

        Data::Dumper certainly cleared that up (I had never used this before, and this will become a favorite.)
        It works both ways: if you can write down your data structure in a manner that looks like Data::Dumper output, then you can be relatively sure your data is at least syntactically correct.

        Anyway. If I get you right, you're reading Set One, which would result in the hash as defined in Listing 1, and then you're reading Set Two, which would alter the original hash to that it becomes the one as defined in Listing Two.

        # Listing One: %hash = ( "123" => "abc", "456" => { # I'm chosing for a hashref here becaus +e "abc" => undef, # eventually we'll possibly replacing t +hose "xyz" => undef, # undefs with something else. } "789" => "abc" );
        # Listing Two: %hash = ( "123" => {"abc" => "zzz"}, "456" => { "abc" => ["xxx", "yyy"], "xyz" => undef # Or [], or 0, or "", or whatev +er }, "789" => {"abc" => "xxx"} );

        But really, this is such a mess that I doubt this is what you want.

        By confining the structure to hashes and avoiding arrays you don't have to worry about duplicates and, by always pre-assigning an anoymous hash, you can also dispense with the bother of turning a scalar value into a hash or an array. I think this is a little simpler and clearer than muba's solution if the structure produced is what you are after.

        use strict; use warnings; use Data::Dumper; open my $univFH, q{<}, \ <<EOD or die qq{open: < HEREDOC: $!\n}; abc 123 abc 456 abc 789 xyz 456 EOD my %univ; while ( <$univFH> ) { my( $lev2Key, $lev1Key ) = split; $univ{ $lev1Key }->{ $lev2Key } = {}; } close $univFH or die qq{close: < HEREDOC: $!\n}; print Data::Dumper->Dumpxs( [ \ %univ ], [ qw{ *univ } ] ); open my $valuesFH, q{<}, \ <<EOD or die qq{open: < HEREDOC: $!\n}; xxx ==> 456->abc xxx ==> 789->abc yyy ==> 456->abc yyy ==> 456->abc zzz ==> 123->abc EOD while ( <$valuesFH> ) { chomp; my( $lev3Key, $upperKeys ) = split m{\s*==>\s*}; my( $lev1Key, $lev2Key) = split m{->}, $upperKeys; $univ{ $lev1Key }->{ $lev2Key }->{ $lev3Key } = 1; } close $valuesFH or die qq{close: < HEREDOC: $!\n}; print Data::Dumper->Dumpxs( [ \ %univ ], [ qw{ *univ } ] );

        The Data::Dumper output, first of the structure after reading the universe file and second after reading the values file and adding its data.

        %univ = ( '456' => { 'abc' => {}, 'xyz' => {} }, '123' => { 'abc' => {} }, '789' => { 'abc' => {} } ); %univ = ( '456' => { 'abc' => { 'xxx' => 1, 'yyy' => 1 }, 'xyz' => {} }, '123' => { 'abc' => { 'zzz' => 1 } }, '789' => { 'abc' => { 'xxx' => 1 } } );

        I hope this is helpful.

        Cheers,

        JohnGG

Re: (another) HoH question
by johngg (Canon) on Jun 23, 2012 at 18:24 UTC

    A hash consists of key/value pairs, just one value per key. Your first statement assigns the value "4" to the key "tree". Your second statement replaces the value "4" with a reference to an anonymous sub-hash containing the key/value pair of "apple" and "6" and in the third statement you replace "6" with a sub-sub-hash containing "red" and "9" as key and value. At each stage you are replacing the previous value with reference to a new anonymous hash which is why your first-level values have disappeared. The fourth statement adds a second key/value pair of "fuji" and "4" to the sub-sub-hash.

    I hope this explains where you are going wrong. Perhaps if you could describe what data structure you are trying to build we could give further advice. You will find the Data::Dumper module useful when trying to visualise the data structures you have created.

    Cheers,

    JohnGG

Re: (another) HoH question
by afoken (Chancellor) on Jun 23, 2012 at 17:46 UTC

    Add the following two lines in fromt of your code:

    use strict; use warnings;

    Run your script. It will complain loudly that variables need explicit package names.This mostly says that your code lacks my in front of the loop variables.

    Add the three mys. Run your script again. Look at the error messages and your code. Compare what you meant with what you really wrote.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
      Almost there.

      I added 'my %hash', and it cleared up the error in the assignments. But it still complains about '$hash->{$key}'. I thought this was covered in 'my %hash' as isn't $hash-{$key} pointing to %hash? What am I missing?

      Thanks!
        ... isn't  $hash-{$key} pointing to  %hash? What am I missing?

        You're missing the point that the expressions  $hash{foo} and  $hash->{foo} refer to different and quite separate hashes.

        >perl -wMstrict -le "my %hash = ( foo => 'bar' ); my $hash = { foo => 'not_bar_at_all' }; ;; print qq{what is foo? $hash{foo}}; print qq{what is foo? $hash->{foo}}; " what is foo? bar what is foo? not_bar_at_all
      Absolutely irrelevant. The issue at hand has absolutely nothing to do with "use strict".
      perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: (another) HoH question
by tobyink (Canon) on Jun 23, 2012 at 18:23 UTC
    use Data::Dumper; my %hash; $hash{tree} = 4; $hash{tree}->{apple} = 6; print Dumper \%hash;

    Run that. When you understand why "4" does not appear in the output, then you will find enlightenment.

    Update: d'oh! "6" doesn't appear in the output. In my defence, the above code was typed on a mobile device where I didn't have a copy of Perl to test it. A flimsy defence, I admit.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

      No matter how I twist or turn it, "4" does appear in the output.

      # With strictures: use strict; use warnings; use Data::Dump 'pp'; my %hash; $hash{tree} = 4; $hash{tree}->{apple} = 6; pp \%hash; __END__ Can't use string ("4") as a HASH ref while "strict refs" in use at G:\ +x.pl line 7.
      # Without strictures: use warnings; use Data::Dump 'pp'; my %hash; $hash{tree} = 4; $hash{tree}->{apple} = 6; pp \%hash; __END__ { tree => 4 }

      No offense, but the real problem is that the OP doesn't seem to understand that the value part of a key/value pair can only hold one value, and consequently the design of his data structure is flawed.

Re: (another) HoH question
by 7stud (Deacon) on Jun 23, 2012 at 19:37 UTC
    Hash: 123->abc 456->abc 456->xyz 789->abc

    But...

    use strict; use warnings; use 5.010; use Data::Dumper; my %hash = ( 123 => 'abc', 456 => 'abc', 456 => 'xyz', 789 => 'abc', ); print Dumper(\%hash); --output:-- $VAR1 = { '456' => 'xyz', '123' => 'abc', '789' => 'abc' };

    The corollary to "keys can have only one value" is "a hash cannot have duplicate keys". If a hash could look like this:

    Hash: 123->abc 456->abc 456->xyz 789->abc

    ...how would perl know what value to return when you said, "Give me the value associated with the key '456'?

      "a hash cannot have duplicate keys". If a hash could look like this: (...snip...) ...how would perl know what value to return when you said, "Give me the value associated with the key '456'?

      True. But a hash value can be an array ref, so in a way you can associate multiple values to a single key. Which, I think, is what is needed here.

Log In?
Username:
Password:

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

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

    No recent polls found