Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Accessing an AoHoAoH

by bradcathey (Prior)
on Jun 05, 2004 at 19:01 UTC ( #361651=perlquestion: print w/replies, xml ) Need Help??

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

Fellow monasterians,

I'm practicing building an AoHoAoH so I can do nested loops in HTML::Template. Though I don't really need to know this for the purposes of H::T, I was curious how to access this data structure:

my @AoH=( { chapter => "Basic", page => ( { paragraph => "lesson1"}, { paragraph => "lesson2"} ) }, { chapter => "Advanced", page => ( { paragraph => "lesson3"}, { paragraph => "lesson4"} ) } );

Tried:

print $AoH[1]{page}{paragraph}; printed: lesson3

But in an attempt to get "lesson4" to print:

print $AoH[1]{page}[1]{paragraph}; printed: not an ARRAY reference

Two questions: 1) is this truly an AoHoAoH? 2) how do I access that 2nd element of the 2nd element? Thank you!


—Brad
"A little yeast leavens the whole dough."

Replies are listed 'Best First'.
Re: Accessing an AoHoAoH
by calin (Deacon) on Jun 05, 2004 at 19:07 UTC

    use [ ... ] instead of ( ... )

      use [ ... ] instead of ( ... )

      right. It's not a simple array but a reference to a nameless array.
      See also   perldoc perllol,   perldoc perlreftut,   perldoc perlref   and   perldoc perldata   for more syntactical details (the usual suspects =))

        It's not a simple array but a reference to a nameless array

        Just to be exact, it's not a "simple array" either. It's a list, which is not quite the same as an array. You can read about the differences by running perldoc -q "a list and" at a command prompt, or by viewing an online copy.

      Just to make things clear (well, it had me stumped for awhile), calin means replacing the () in the values of your page hashes by []. That way, you're assigning an anonymous array to the value of the hash, instead of a list, as you are currently doing.

      BTW, the @AoH assignment throws a warning:

      Odd number of elements in anonymous hash at aoh.pl line 21.

      That is because of the parentheses instead of square brackets.

      As to your second question: it's an array of hashes one of whose values is an array of hashes. So, in a sense, it is a AoHoAoH :)

      CU
      Robartes-

        Thanks robartes, for the explanation. I had changed all the parens to brackets and got warnings (after you reminded me to turn those on, duh). Anyway got it to work using the brackets as you indicated.

        —Brad
        "A little yeast leavens the whole dough."
      Thanks calin, that did it! I guess I'm still a little confused about when to use parens and when to use brackets. In reading The Perl Cookbook and Programming Perl, it appears that I might have been confusing the building of a HoA and an AoH, the former using the brackets, and the latter using parens.

      —Brad
      "A little yeast leavens the whole dough."

        As a "i just now made this up rule of thumb", use braces when parens won't do. Parens are just a fragile container for lists, they will flatten by default:

        my @one_d = ( (1,2,3), (4,5,6), (7,8,9) );
        is really just a single list. You have to use braces:
        my @two_d = ( [1,2,3], [4,5,6], [7,8,9] );
        I prefer to use a reference to an anonymous array for the outside container, maily because there is no mixing of parens and braces, square or curly:
        my $two_d = [ [1,2,3], [4,5,6], [7,8,9] ];
        When in doubt, always consult Data::Dumper. Always! If you had used Data::Dumper on your data structure, you would have seen that the problem was within it:
        print Dumper \@AoH; __END__ (it's an AoHoH ... not an AoHoAoH) $VAR1 = [ { 'page' => { 'paragraph' => 'lesson1' }, 'chapter' => 'Basic', 'HASH(0x8638dec)' => undef }, { 'HASH(0x86fee04)' => undef, 'page' => { 'paragraph' => 'lesson3' }, 'chapter' => 'Advanced' } ];
        Finally, print out a copy of References quick reference. And don't forget about Data::Dumper! :)

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)
        
        I guess I'm still a little confused about when to use parens and when to use brackets.
        Parens build a list, brackets build an anonymous array, and return a reference to it. Hashes can only contain scalars as values, so a list won't ever do (except when it contains only one item — but then you don't need a list). An array reference is a scalar, so that will work. As it's the only thing that actually does work properly, Perl's syntax has been optimized to ease access to array references as hash and array values.

        $x->{'foo'}[1] actually means: use $x as a hash reference, get the value associated with the string 'foo'. Use this as an array reference and access the second element (with index 1) from it.

        Actually you can even split this up into:

        $aref = $x->{'foo'}; $aref->[1]
        provided you didn't need autovivification, which were to happen in case $x->{'foo'} was undefined, in the former case — but not in the latter.
        If you want to nest something, use brackets. Nested parentheses are meaningless in lists; only the outermost set defines any structure.

        The PerlMonk tr/// Advocate
Re: Accessing an AoHoAoH
by dimar (Curate) on Jun 05, 2004 at 21:17 UTC

    A couple of thoughts:

    1) As a stylistic preference, whenever I construct a nested data variable in perl, I always start with a scalar variable at the top, like '$dataroot'. I like the way it looks and it's flexible, since a scalar can hold a reference to anything. You'll notice that Data::Dumper does the same thing when it spits out ($VAR1 $VAR2 etc..) as the topmost 'container' of the output.

    2) Using the style mentioned above helps me focus more on the *meaning* of what it is I am really storing, helps keeps things readable. Given this style, your example becomes this slightly different code ...

    use strict; use warnings; my $chapters = [ { title => 'Basic', page => [ { paragraph => 'lesson1'}, { paragraph => 'lesson2'}, ], }, { title => 'Advanced', page => [ { paragraph => 'lesson3'}, { paragraph => 'lesson4'}, ], }, ]; ### since we started with a scalar to hold ### an anonymous array ref, we have to ### use the little 'arrow' notation print $chapters->[0]{title}; ### basic print "\n---------------\n"; print $chapters->[1]{title}; ### advanced print "\n---------------\n"; print $chapters->[1]{page}[0]{paragraph}; ### lesson3 print "\n---------------\n"; print $chapters->[1]{page}[1]{paragraph}; ### lesson4 print "\n---------------\n";
      Good stuff, scooterm. Observation: with the scalar, it's brackets all the way. Question: does assigning the data structure to the scalar automatically make it a reference? So, there's no need to:
      $chapters = \$chapters;
      Right? Thanks.

      —Brad
      "A little yeast leavens the whole dough."

        You guessed it. You get to use square brackets for an array, and curly braces for an hash, and the syntax stays consistent thru the whole thing, no matter how deeply your data gets nested.

        The only time it gets a little 'funky' is when you want to treat your data like a regular array, for example, in a foreach loop. Actually, perldsc lays it all out.

        ### here we tell perl to treat the scalar as a ref ### to an array. foreach my $item (@{$chapters}){ print $item->{title}; }

        Side note: Data::Dumper is absolutely a plus. Sprinkle it into every script where you use a complex variable, in fact, sprinkle it everywhere. It's very useful.

        Assigning a reference to a scalar.. makes the scalar contain a reference! Theres no need to "initialize with a reference to itself as you seem to think. You do need to actually initialize something with a reference before you try to dereference it, unless strict is off.
Re: Accessing an AoHoAoH
by CountZero (Bishop) on Jun 05, 2004 at 19:48 UTC
    Do not assign the reference to your structure to an array (@AoH) but to a scalar. Putting an array creates an extra level of an array in it which you do not need. Use Data::Dumper to see this.

    CountZero

    "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law

      CountZero, not sure I understand. Ran Data::Dumper and got:
      $VAR1 = { 'chapter' => 'Basic', 'page' => [ { 'paragraph' => 'lesson1' }, { 'paragraph' => 'lesson2' } ] }; $VAR2 = { 'chapter' => 'Advanced', 'page' => [ { 'paragraph' => 'lesson3' }, { 'paragraph' => 'lesson4' } ] };
      What am I missing? Thanks

      —Brad
      "A little yeast leavens the whole dough."

        That's what you should get if you write something like dump @AoH; because @AoH "flattens" to a one-element list in the list context of the function call. (This was just a guess, but I'm sure in what I'll write below.)

        This way, however, you can not easily access the contents of the deep datastructure, as you can't index the array in such a way that it flattens automatically, so you have to use a 0 as a first array index, just as CountZero said.

        To change, either use a scalar variable, or (perhaps better) use an array variable, just assign it like @AoH= ({...}, {...}, ...);, not @AoH= [{...}, {...}, ...];.

        Try print Dump(\@AoH);

        Then you see the extra level of the @AoH array

        CountZero

        "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law

Re: Accessing an AoHoAoH
by FoxtrotUniform (Prior) on Jun 07, 2004 at 18:30 UTC

    On the subject of style: I like to decompose nested-structure dereferences into "sensical" single-layer dereferences, as below:

    #! /usr/bin/perl -w use strict; my @AoH=( { chapter => "Basic", page => [ { paragraph => "lesson1"}, { paragraph => "lesson2"} ] }, { chapter => "Advanced", page => [ { paragraph => "lesson3"}, { paragraph => "lesson4"} ] } ); foreach my $ch (@AoH) { print $ch->{'chapter'}, "\n"; my $page = $ch->{'page'}; foreach my $paras (@$page) { print "\t", $paras->{'paragraph'}, "\n"; } }

    I find this makes code using these structures easier to read, although longer. It's a judgement call as to whether your structure is dense enough to warrant the extra code.

    (I also like to use -> for every deref and single-quote every hash key, but that's a whole different issue....)

    --
    F o x t r o t U n i f o r m
    Found a typo in this node? /msg me
    % man 3 strfry

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (1)
As of 2022-10-01 20:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My preferred way to holiday/vacation is:











    Results (3 votes). Check out past polls.

    Notices?