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

Hi Monks,

I see that perldsc shows one way to generate an array of hashes from a file, is like this:

# reading from file # format: LEAD=fred FRIEND=barney while ( <> ) { $rec = {}; for $field ( split ) { ($key, $value) = split /=/, $field; $rec->{$key} = $value; } push @AoH, $rec; }

That works, but I'd like to know how to do that without using references? I would have thought something like this (I'll comment out the parts I'm changing):

while ( <> ) { #$rec = {}; $rec = (); for $field ( split ) { ($key, $value) = split /=/, $field; #$rec->{$key} = $value; $rec{$key} = $value; } push @AoH, $rec; #? }
But that doesn't work.

How can I do the same code the same thing without using references?

Thanks!

Replies are listed 'Best First'.
Re: Generate Array of Hashes WITHOUT References
by ikegami (Patriarch) on Sep 09, 2014 at 05:04 UTC

    Elements of arrays are scalars. You cannot store a hash in $a[0] any more than you could in $x.

    That said, you could delay creating the reference until the last moment, if that's what you're looking for.

    push @AoH, \%rec;

    Update: Made the answer more complete by adding the second half.

Re: Generate Array of Hashes WITHOUT References
by tobyink (Canon) on Sep 09, 2014 at 11:04 UTC

    An array is a list of scalars. Hashes are not scalars. Ergo, arrays cannot contain hashes.

    There are two workarounds if you want to store a hash in an array:

    1. References are scalars. Therefore, you can store a reference to a hash in an array.

    2. Strings are scalars. If you can serialize the hash into a string, you can store that in an array.

    There is no #3, and it is almost always a terrible idea to choose #2 over #1.

    Why exactly are you trying to avoid using references?

      Thanks for that, toby.

      I like to avoid references where there's a simple alternative, because in my case it saves brain power. I seemed to recall that this could be done without this kind of method:
        $rec->{$key} = $value;
      and it seems I was right, as I can use:
        $rec{$key} = $value;
      and I see no advantage of the former method, as the latter is more familiar, concise, and simpler for me, so that's what I'll use. It seems the only place I need to refer to the hash as a reference is when I push it onto the array, which I'm OK with.

Re: Generate Array of Hashes WITHOUT References
by b4swine (Pilgrim) on Sep 09, 2014 at 05:57 UTC

    The question has been answered already. I thought I would add a little bit more explanation.

    When we write %hash = ('a', 'b', 'c', 'd'); this is the same as saying %hash = ('a' => 'b', 'c' => 'd'); because the => are just fat commas. So, when you push the hash onto an array, push @AoH, %hash this is just the same as push @AoH, ('a', 'b', 'c', 'd'); which will simply push the elements one by one onto @AoH, losing the structure of the hash or array that you are pushing.

    Small technical point. You don't know if at the end of this you get @AoH = ('c', 'd', 'a', 'b'); or @AoH = ('a', 'b', 'c', 'd'); because the ordering of the keys in a hash is arbitrary.

    This is the whole reason why references were added in Perl 5.

      An additional note on fat commas =>: The left "operand" of a fat comma can be a bareword: (a => 'b', c => 'd')

      Of course, there are risks to using bare words, so another alternative would be qw/a b c d/ While it is the least typing, it's intention is not as clear as when using fat commas.

Re: Generate Array of Hashes WITHOUT References
by Hameed (Acolyte) on Sep 09, 2014 at 06:03 UTC
    You can try this way:
    #!/usr/local/bin/perl use strict; use warnings; use Data::Dumper; my %rec = (); my $field; my $value; my $key; my @AoH = (); while ( <DATA> ) { chomp; $field = $_; ($key, $value) = split /=/, $field; $rec{$key} = $value; } push @AoH, \%rec; print Dumper (\@AoH); __DATA__ Key1=Value1 Key2=Value2 Key3=Value3
Re: Generate Array of Hashes WITHOUT References
by LanX (Saint) on Sep 09, 2014 at 08:17 UTC
    Fascinating how most replyers misunderstood you're question, cause they read the title but not the code.

    This is wrong

    $rec=();

    You need to write %rec=() because you don't want references.

    WHY don't you use warnings ??

    Cheers Rolf

    (addicted to the Perl Programming Language and ☆☆☆☆ :)

      Fascinating how most replyers misunderstood you're question, cause they read the title but not the code.
      I fail to see how explanations that you can only store scalars (such as references, but not hashes) in an array imply a misunderstanding of the questions asked in the OP, which, for reference, are: "I'd like to know how to do that without using references?" and "How can I do the same code the same thing without using references?"

      You need to write %rec=() because you don't want references.
      ...and that still won't work, because the code ultimately does a push @AoH, $rec; Changing $rec to %rec will just plain not work because, even without strict, $rec and %rec are two different variables with no direct connection between them, so it would just be pushing an undef into @AoH.

      Of course, that could easily be fixed by changing the push to push @AoH, %rec;, which will work... but it will do something completely different than what the OP wants, as explained in b4swine's answer.

      Guess you misunderstood the question because you started reading the code, then addressed the first problem you saw without bothering to read all the code.

      The answer to the question actually asked, as previous posters have correctly understood, is "You can't do that without using references."

        > I fail to see how explanations that you can only store scalars ..

        I read the question as:

        "How can I avoid the dereferencing in $rec->{$key}=$value " and not "How can I store non-ref hashes in arrays."

        He tried $rec{$key}=$value but failed b/c he used %rec now.

        > ...and that still won't work, because the code ultimately does a push @AoH, $rec;

        true, but this was already demonstrated by Hameed but w/o explanation ... an explanation I added, without testing the code.

        Telling the OP (like most did here) that he can ultimately only store refs doesn't answer his question how to avoid dereferencing BEFORE storing.

        Of course most of his errors would have easily be visible if he used strict and warnings .

        But - alas - he mentioned that he took the code from the perldocs which (like I checked) don't use these pragmas either. :-(

        Finally I posted from mobile, it's hard to give working code from Android.

        Cheers Rolf

        (addicted to the Perl Programming Language and ☆☆☆☆ :)

      That is only half a third of it LanX :)
      #!/usr/bin/perl -- # reading from file # format: LEAD=fred FRIEND=barney # # # #!/usr/bin/perl -- use strict; use warnings; use Data::Dump qw/ dd /; my @AoH; while ( <> ) { my %rec; for my $field ( split ) { my( $key, $value ) = split /=/, $field; $rec{$key} = $value; ## second half } push @AoH, \%rec; } dd( \@AoH ); __END__
        Hameed already posted this.

        Cheers Rolf

        (addicted to the Perl Programming Language and ☆☆☆☆ :)

        Good answer, Anonymous Monk.
        Thank you!
      Thanks for your reply, Rolf.

      > Fascinating how most replyers misunderstood you're question, cause they read the title but not the code.

      I didn't notice. Which posts are you refering to?

      > This is wrong
      > $rec=();
      > You need to write %rec=() because you don't want references.

      Sorry - typo on my part (that is what I meant to type). However, but after making that change in my code below:

      #!/usr/bin/perl use Data::Dumper; use warnings; while ( <DATA> ) { #$rec = {}; #$rec = (); %rec = (); for $field ( split ) { ($key, $value) = split /=/, $field; $rec{$key} = $value; } push @AoH, \%rec; } print Dumper(\@AoH); __DATA__ A=1 B=2 C=3 Y=4 Z=5
      ...which includes the change of push'ing \%rec, I get this output:
      $VAR1 = [ { 'Z' => '5', 'Y' => '4' }, $VAR1->[0] ];
      Which is wrong, but if I change "%rec = ();" to "my %rec;" (as per Anonymous Monk's solution), I get what I want:
      $VAR1 = [ { 'A' => '1', 'C' => '3', 'B' => '2' }, { 'Z' => '5', 'Y' => '4' } ];
      Any ideas why? What's the significant difference between "%rec = ();" and "my %rec;" in this case?

      > WHY don't you use warnings ??

      No good reason (force of (bad) habit), but apart from the reason you mentioned in a later post, having tried "use warnings" in my code now (as above), it looks as if it would not have reported any problem. "use strict" may have helped.

        $VAR1 = [ { 'Z' => '5', 'Y' => '4' }, $VAR1->[0] ];

        The second element refers to the first element of the array reference. If $Data::Dumper::Deepcopy = 1;, then you will see that both array elements are the same.

        The problem is of variable scoping; see Coping with Scoping. I will try to explain but do wait for better|clearer explanation.

        Without my function, the hash %rec has scope file-wide (outside of the loop). The reference to the hash refers to that hash, the only copy. So modifying the only copy of the hash overwrites previous values (see by for { ... } print Dumper( \%rec ) ;). As the reference to the hash is saved twice, same values are repeated in Dumper() output.

        Use of my function inside the while loop limits the scope of %rec within the loop. On each iteration a *spanking new* hash is allocated, saving the values as references to each separate hash in the array as expected.

Re: Generate Array of Hashes WITHOUT References
by vinoth.ree (Monsignor) on Sep 09, 2014 at 05:38 UTC

    In Perl, array and hash members must be a single value. Before Perl 5.0, there was no (easy) way to do what you want.

    However, in Perl 5 you can now use a reference to your hash. A reference is simply the memory location where the item is being stored. To get a reference, you put a backslash in front of the variable


    All is well
Re: Generate Array of Hashes WITHOUT References
by Anonymous Monk on Sep 09, 2014 at 07:10 UTC

    What is the reason for avoiding references?

      I like to avoid them where there's a simple alternative, because in my case it saves brain power.
        I'd say that's actually a reason for not avoiding them - use them, get familiar with them, and eventually you'll cease being bothered by them. You'll learn and gain a better understanding of and feeling for Perl.
Re: Generate Array of Hashes WITHOUT References
by Laurent_R (Canon) on Sep 10, 2014 at 06:22 UTC
    I remember that the first times I had to use nested data structures, I did not know much about references and I certainly did not understand that, say, an AoH really was an array ofr references to anonymous hashes. That did not prevent me from using some form of baby Perl syntax to use successfully AoH or HoH with simple statements such as:
    $foo[5]{'bar'} = "baz";
    So you can use, to a certain extent, such data structures without having to care abbout the fact that you are really dealing with an array of references, and that's what I cared mostly at the time. Now, of course, when you need to go further, you need a deepter understanding of how the structure works, and I had to go through this process. But when I started to use such nested data structure, I was certainly happy that I could do it without having to go through the brain stress of using this structure AND having to understand references.