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

My code is object oriented..$mon_obj->{'FILE_DATA'} is a reference to an array, each element of the array represents a line in a text file. Periodically I delete elements(lines in the file) by setting them to undef(or I could use delete $array[$x]). After deleting a number of elements, I wanted to do a sort of 'garbage collection' with the elements that are set to undef. Below is the best method I could come up with for a way that actually deletes the element. It seems easier and better(to me) to ensure the undef array elements are tossed out, as opposed to adding a defined($mon_obj->{'FILE_DATA'}->[$x]) check within every structure in my code that loops over this array. Does all this seem reasonable? Do share any/all insights. thanx.
my $x; my $counter; for($x = 0; $x < @{$mon_obj->{'FILE_DATA'}}; $x++) { unless($mon_obj->{'FILE_DATA'}->[$x]) ## if undef incr. the gap { $counter++; } if($counter > 0) ## copy the data from $x + gap to $x { $mon_obj->{'FILE_DATA'}->[$x] = $mon_obj->{'FILE_DATA'}->[$x + $c +ounter]; } } for($x = 0; $x < $counter; $x++) { pop @{$mon_obj->{'FILE_DATA'}; ## pop into oblivion }

Replies are listed 'Best First'.
Re: deleting array elements(efficiency/critique)
by chipmunk (Parson) on Dec 27, 2000 at 20:50 UTC
    The main drawback I see with your code is that it copies elements from past the end of the array.

    Here's a solution where the source index is the loop variable, and the destination follows behind it:

    my $p = 0; for (my $q = 0; $q < @{$mon_obj->{'FILE_DATA'}}; ++$q) { if (defined $mon_obj->{'FILE_DATA'}->[$q]) { $mon_obj->{'FILE_DATA'}->[$p] = $mon_obj->{'FILE_DATA'}->[$q] if $p != $q; $p++; } } splice @{$mon_obj->{'FILE_DATA'}}, $p;
      chipmunk-
      Nice re-write of my code..tad more thought involved to understand it, but it is quite beautiful once it clicks in the head.

      Although I think I'll end up using something like the replies that used grep, since it seems a shorter/clearer/rather efficient means of doing it.
Re: deleting array elements(efficiency/critique)
by I0 (Priest) on Dec 27, 2000 at 20:50 UTC
    @{$mon_obj->{FILE_DATA}} = grep{$_}@{$mon_obj->{FILE_DATA}}

      It's unclear whether or not the array can contain a valid line which consists of the empty string or the character '0'. either of these possibilities would break your code. This problem can be fixed with:

      @{$mon_obj->{FILE_DATA}} = grep { defined $_ } @{$mon_obj->{FILE_DATA}};
      --
      <http://www.dave.org.uk>

      "Perl makes the fun jobs fun
      and the boring jobs bearable" - me

        Yes, that's an obvious refinement, which the original loop did not contain,
        presumably because '' and '0' are either not present or not valid
        so I preserved that behavior in the grep condition.
Re: deleting array elements(efficiency/critique)
by ichimunki (Priest) on Dec 27, 2000 at 21:24 UTC
    You are trying to remove undefined elements in a list?
    my @list = ('foo', 'bar', undef, 'batz'); my @clean_list; for (@list) {if (defined) { push (@clean_list, $_); } } @list = @clean_list;
    Change the first and last lines to point to your object's list and you're all set. There may be better ways to do this, but this works.

    Update:ichimunki notes the grep examples and wishes he'd thought of that. :)
Re: deleting array elements(efficiency/critique)
by repson (Chaplain) on Dec 31, 2000 at 06:58 UTC
    Instead of deleting or undefing elements you could use splice.
    # delete $array[$x]; # $array[$x] = undef; splice @array, $x, 1;
    That way you don't have to go through a garbage collection stage later, but you may not gain much in speed since splice is slower that delete.