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

I am looping through the contents of a file and generating a hash inside that loop for each matched string I find. After I make the hash, I push a reference to that hash onto an array.

After I push all 400+ hash references onto my array, it appears that somehow all of the references in the array point to the very last hash that was pushed onto the array. I can't figure out why this is happening.

I can run the debugger and watch my array of hash references populate exactly like I want, and I can step all the way through the looping code to the end of the loop and my array of hash references (and all the data those references point to in the hash) look correct.

Then as soon as I leave the block looping code, each hash reference in my array points to the hash that was pushed onto the array last.

Why is this happening? Any help is greatly appreciated. I have included a snippet of my code below:

=================================
### $html contains contents of a file while($html =~ <some regex here>){ $player{'gp'}=$1; $player{'min'}=$2; $player{'fgm'}=$3; $player{'fga'}=$4; $player{'fgp'}=$5; $player{'tpm'}=$6; $player{'tpa'}=$7; $player{'tpp'}=$8; push (@players, \%player); $html=$'; } ###Once I get here, every hash ref in the array points to ###the same +hash (the last one to be pushed onto the array. ###Each of the follo +wing lines then prints out ###the same data. print "Size of \@players: ".$#players." Element 0 is: $players[0]{ +'min'} "."\n"; print "Size of \@players: ".$#players." Element 1 is: $players[1]{ +'min'} "."\n"; print "Size of \@players: ".$#players." Element 2 is: $players\[2\ +]{'min'} "."\n";
=================================

Again, thank you in advance for any help that is provided.

Replies are listed 'Best First'.
Re: Array of Hashes question
by pg (Canon) on Oct 30, 2004 at 05:52 UTC

    Put your "my %player;" inside the while loop. Try with the following code, then uncomment that "my %player;" outside the loop, comment out the inside one and try again.

    use Data::Dumper; use strict; use warnings; my @players; #my %player; for my $index (1 .. 10){ my %player; $player{'a'}=$index; push (@players, \%player); } print Dumper(\@players);
      Fantastic.

      Thank you so much, pg and bobf. Your explanations were clear and correct. My problem is solved.

Re: Array of Hashes question
by bobf (Monsignor) on Oct 30, 2004 at 06:10 UTC

    If %player is declared outside of the while loop, you're using the same hash for each iteration so when you push the hashref onto the array, you're pushing a ref to the same %player each time. Therefore, you end up with an array filled with hashrefs all referring to the same hash, and that is why all of the hashrefs have the same values. If you want to push a new hashref each time, declare a new hash inside the while loop.

    Here is some code to illustrate the difference between declaring the hash outside of the loop vs declaring it inside the loop:

    use strict; use warnings; my ( %outhash, @array_out, @array_in ); for( my $i = 0; $i < 5; $i++ ) { $outhash{key1} = $i; push( @array_out, \%outhash ); my %inhash; $inhash{key1} = $i; push( @array_in, \%inhash ); } print "array_out:\n"; foreach my $hashref ( @array_out ) { print "$hashref\n"; } print "array_in:\n"; foreach my $hashref ( @array_in ) { print "$hashref\n"; } output: array_out: HASH(0x183285c) HASH(0x183285c) HASH(0x183285c) HASH(0x183285c) HASH(0x183285c) array_in: HASH(0x198c014) HASH(0x1835164) HASH(0x18351dc) HASH(0x1835254) HASH(0x18352cc)
    @array_out is filled with refs to the same hash, but each ref in @array_in is unique.