in reply to Re: Using grep to remove array element from HoAoH leaving empty array-hash element
in thread Using grep to remove array element from HoAoH leaving empty array-hash element

As far as I am able to tell, the warning is being generated in the grep. Splitting it in to seperate lines in the code said that the warning was in the second occurence of @{$bros->{$yr}} - the array being grepped.

The code I'm using to initialize the whole structure is:

my $bros= {'1958' => [], '1959' => [], '1960' => [], '1961' => [], '1962' => [], '1963' => [], '1964' => []};

since I was lazy and wanted to pre-build my data structure.

Here's the Data::Dumper listing of before and after the grep:

before grep: $VAR1 = \[ { 'state' => 'OR', 'last' => 'Bertelson', 'first' => 'Gordon', }, { 'state' => '', 'last' => 'Blaise', 'first' => 'Larry', }, { 'state' => 'OR', 'last' => 'Douda', 'first' => 'Henry B.', }, { 'state' => 'NSW 221', ' => 'Australia', 'last' => 'McHolick', 'first' => 'Dwane', }, { 'state' => 'OR', 'last' => 'Murray', 'first' => 'Melvin', }, { 'state' => 'OR', 'last' => 'Perry', 'first' => 'Larry', }, { 'state' => '', 'last' => 'Peterson', 'first' => 'Ken', }, { 'state' => 'OR', 'last' => 'Rianda', 'first' => 'Dave', }, { 'state' => 'OR', 'last' => 'Steffanoff', 'first' => 'Nick', }, { 'state' => 'CA', 'last' => 'Turner', 'first' => 'Paul', }, { 'state' => 'OR', 'last' => 'Wilson', 'first' => 'Dick', }, {} ]; after grep: $VAR1 = \[ { 'state' => 'OR', 'last' => 'Bertelson', 'first' => 'Gordon', }, { 'state' => '', 'last' => 'Blaise', 'first' => 'Larry', }, { 'state' => 'OR', 'last' => 'Douda', 'first' => 'Henry B.', }, { 'state' => 'NSW 221', 'last' => 'McHolick', 'first' => 'Dwane', }, { 'state' => 'OR', 'last' => 'Murray', 'first' => 'Melvin', }, { 'state' => 'OR', 'last' => 'Perry', 'first' => 'Larry', }, { 'state' => '', 'last' => 'Peterson', 'first' => 'Ken', }, { 'state' => 'OR', 'last' => 'Rianda', 'first' => 'Dave', }, { 'state' => 'OR', 'last' => 'Steffanoff', 'first' => 'Nick', }, { 'state' => 'OR', 'last' => 'Wilson', 'first' => 'Dick', }, {} <- here's the empty hash ];

so you can see exactly what I'm saying by there's an empty hash where Paul Turner's information was.

As far as removing the element, I thought using the negative grep would find everything that didn't match what I wanted and put it in the array. I thought I was on the right track with the negative grep but I don't understand the empty hash in the array still being there.

My assumption is that grep is choking for whatever reason on that empty hash, so is a negative grep the way to go here?

Useless trivia: In the 2004 Las Vegas phone book there are approximately 28 pages of ads for massage, but almost 200 for lawyers.

Replies are listed 'Best First'.
Re^3: Using grep to remove array element from HoAoH leaving empty array-hash element
by Aristotle (Chancellor) on Aug 11, 2004 at 05:12 UTC

    Err. There is an empty hash at the bottom of your before-grep dump as well. Is that a posting mistake, or is it actually there during your code's execution?

    Makeshifts last the longest.

      Good catch! That is being added when the program runs, it wasn't a posting error. Looks like I need to get my eyes checked. :)

      I've narrowed it down to this bit of code, but for the life of me I can't see how or why it would be adding the empty hash. I've also noticed that I lose the data in the 1958 array after the first iteration of the for loop.

      Basically I'm going through each of the years I have data for and seeing if my match appears in any of those years. If it does, it gets put in to @res for display in a list.

      Here's the offending code:

      for $find qw((1958 1959 1960 1961 1962 1963 1964)){ $index = 0; # index is defined to go down the array of each year while (defined $bros->{$find}->[$index]->{$key}){ if ($bros->{$find}->[$index]->{$key} =~ /$data/i){ # since we found one, we push the brothers data structure a +nd # year of graduation in to the results hash push (@res,{brother => $bros->{$find}->[$index], year => $f +ind}); } # end if $index++; } # end while } # end find

      I'd appreciate anything you could suggest as to where this might be adding the empty hash!

      Useless trivia: In the 2004 Las Vegas phone book there are approximately 28 pages of ads for massage, but almost 200 for lawyers.

        You are running into autovivification.

        $ perl -le'undef $_; defined $_->{0}; print $_' HASH(0x813f1c8)

        Whenever you try to dereference an undefined value, Perl will helpfully summon an anonymous data type of the corresponding type and store it there. In your case, the culprit is probably

        while( defined $bros->{ $find }->[ $index ]->{ $key } ) { # ... }

        If you use exists there instead, your ghost hashes should go away. Actually, no, they won't. The hash is summoned by the mere attempt at dereferencing. You have to make sure that $bros->{ $find }->[ $index ] exists before attempting a dereference:

        while( ref $bros->{ $find }->[ $index ] and defined $bros->{ $find }->[ $index ]->{ $key } ) { # ... }

        However, in Perl, it's almost always a mistake to use a counter variable such as $index when you only ever use it as an index into a single structure. This is one of the red flags mentioned in Mark-Jason Dominus' excellent Program Repair Shop and Red Flags article series. You could have avoided the issue altogether by rewriting your inner loop to a foreach:

        for my $year ( qw( 1958 1959 1960 1961 1962 1963 1964 ) ) { for my $film ( @{ $bros->{ $year } } ) { push @res, { brother => $film, year => $year } if $film->{ $key } =~ /$data/i; } }

        Depending on taste that can be transformed to a grep:

        for my $year ( qw( 1958 1959 1960 1961 1962 1963 1964 ) ) { push @res, ( map +{ brother => $_, year => $year }, grep $_->{ $key } =~ /$data/i, @{ $bros->{ $year } }, ); }

        Makeshifts last the longest.