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

Hi

I have an array of hashes. Here is the output from Data::Dumper for some of it

$VAR1 = { 'divs' => [ 'Motion-work wheels after servicing', '2048x1536', 'DSCN0095.JPG' ], 'href' => '/p19581477.html' };
$VAR2 = { 'divs' => [ 'Movement without motion-work', '2048x1536', 'DSCN0096.JPG' ], 'href' => '/p19581478.html' }; $VAR3 = { 'divs' => [ 'Top view of bare movement', '2048x1536', 'DSCN0097.JPG' ], 'href' => '/p19581479.html' }; $VAR4 = { 'divs' => [ 'Movement without centre plate (1)', '2048x1536', 'DSCN0098.JPG' ], 'href' => '/p19581480.html' };

Now I first need to check my understanding of this is correct. Please correct me where I am wrong.

As far as I can determine, an array element here consists of a hash reference. The hash so referenced has two keys, 'divs' and 'href'. The value of the 'href' element is a string. The value of the 'divs' is an array reference.

This code works as I expect.

my $test = $pictures[2]; print $test."\n"; foreach my $ky (keys %$test) { print $ky." = ".$test->{$ky}."\n"; } my $yy = $test->{'divs'}; foreach my $xx (@$yy) { print $xx."\n";}

producing this output

HASH(0x831fa5c) divs = ARRAY(0x831da2c) href = /p19581479.html Top view of bare movement 2048x1536 DSCN0097.JPG

This code doesn't work

my $i = 0; foreach my $pic (@pictures) { print $i." href : ".$pic->{'href'}." divs : "; my $tmp = $pic->{'divs'}; #print @tmp." : "; #print $tmp," : ",@$tmp; #foreach my $div ($pic->{"divs"}) { print .", ".$div;} foreach my $xy (@$tmp) { print .", ".$xy; } print "\n"; $i++; }

producing this output

0 href : /p19581477.html divs : 1 href : /p19581478.html divs : 2 href : /p19581479.html divs : 3 href : /p19581480.html divs :

Spot the lack of text in the ouput.

I am at a loss to see what the difference is between

my $tmp = $pic->{'divs'}; foreach my $xy (@$tmp) { print .", ".$xy; }

and

my $yy = $test->{'divs'}; foreach my $xx (@$yy) { print $xx."\n";}

The only difference is the way in which the array element is selected being either a direct assigment my $test = $pictures[2]; or stepping through each element with a foreach loop.

Can anyone please explain what I am not understanding here?

Thanks in advance and looking forward to enlightenment.

Replies are listed 'Best First'.
Re: Arrays of hashes which have arrays
by ikegami (Patriarch) on Jan 26, 2009 at 22:39 UTC

    Use use warnings;!!! You would have gotten at least the first of

    Useless use of concatenation (.) or string in void context Use of uninitialized value in print
    print .", ".$xy; ^ |

    The above means

    print() . ", " . $xy;

    and thus

    print($_) . ", " . $xy;

    By the way, when dumping an array, use
    print Dumper \@array;
    instead of
    print Dumper @array;

    Update: Added warning.

      I am using warnings .. I only posted the snippets that were causing me grief. I misinterpreted those warnings to mean I was attempting to print a variable with no value.

      Thank you very much for your help

        "Useless use in void context" is not a comment about the operands. It means the result of an operation is being discarded without being used. In this case, the result of the concatenation of the value returned by print and what follows is being discarded.

        The following would also issue the warning

        @a = 4, 5;
        because Perl interprets that as
        ( @a = 4 ), 5;

        The result of the list operator is being discarded. (I'm making the likely assumption that the above is being evaluated in void context.)

Re: Arrays of hashes which have arrays
by kyle (Abbot) on Jan 26, 2009 at 22:40 UTC

    The only difference is the way in which the array element is selected...

    Another difference is that the second one doesn't have a concatenation operator right after print. Using B::Deparse (with -p), we see that it is interpreted this way:

    foreach my($xy) (@$tmp) { ((print($_) . ', ') . $xy); }

    Remove that dot, and it should work the way you want it to.

Re: Arrays of hashes which have arrays
by clwolfe (Scribe) on Jan 27, 2009 at 18:27 UTC
    Hello all,

    As the other monks have answered your question, I thought I might offer a tip. When printing the contents of an array, or stringifying an array in general, it's a lot easier (and more idiomatic) to use join.

    So instead of:

    my $yy = $test->{'divs'}; foreach my $xx (@$yy) { print $xx."\n";}

    try:

    my $yy = $test->{'divs'}; print ((join "\n", @$yy) . "\n");
    The extra parens are required to prevent print being interpreted as a function and seeing only the join, not the extra "\n".

    You also might want to explore more about {} syntax for dereferencing structures. The Camel Book has an excellent overview of the various syntaxes. Here's an example, eliminating the need for $yy:

    foreach my $xx (@{$test->{divs}}) { print $xx."\n";} print ((join "\n", @{$test->{divs}}) . "\n");
    Of course, it can be argued that these sorts of idioms reduce legibility. It's a matter of taste.

    --Clinton

      my $yy = $test->{'divs'}; print ((join "\n", @$yy) . "\n");

      The extra parens are required to prevent print being interpreted as a function and seeing only the join, not the extra "\n".

      You only really need one pair of parentheses and those should be around the arguments to join, and the concatenation (.) would, I think, be better as a comma since print is happy to work with a list.

      my $yy = $test->{'divs'}; print join( "\n", @$yy ), "\n";

      Another way to do this is to localise the list separator to a newline and, as you say, avoid the $yy temporary variable.

      print do{ local $" = qq{\n}; qq{@{ $test->{ divs } }\n} };

      I hope this is of interest. (Note, the use of quoting constructs, q{...} instead of '...' and qq{...} instead of "..." is a habit of mine so that one-liners work in *nix shells and at MS command prompts.)

      Cheers,

      JohnGG