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

Hi all monks, I have a problem. When reading all the perl docs on deleteing array elements they say somthing like this:
my @arr = (1, 2, 3); delete $arr[0]; however I have set of functions looking like this ### main ### fetch the cashflows from input file and map the values from the co +nfig file my @cfs = &fetch_cfs; ### manipulate the cashflow values @cfs = &man_cfs(\@cfs,\@valid_ports); ######################################## sub man_cfs { my ($cashflow,$valid_ports) = @_; foreach my $cf (@$cashflow) { if ($cf->{'portfolio_id'} eq "") { delete $cashflow[$count]; next; }
... mening I send in an array reference into the man_cfs function. But when I do the deletion nothing happens with $cashflow However @cashflow gets the element deleted... Why is this?? If I send in a referens shouldn't it be possible to delete the element using the techniques in the books, and why is the @cashflow populated?? Would be most grateful for answers, cause I have a deadline tomorrow *YNF*...

Replies are listed 'Best First'.
Re: deleteing array elements I get mad!!
by davorg (Chancellor) on Oct 03, 2006 at 08:36 UTC

    This is one of those (many) places where use strict would have told you what the problem is.

    What you're doing is effectively this:

    use strict; use warnings; my $arr_ref = [1, 2, 3]; delete $arr_ref[0];

    And running that gives the error:

    Global symbol "@arr_ref" requires explicit package name

    You have an array reference in $cashflow, but you're treating it as if it's an array.

    --
    <http://dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

      Thanks Well usually I use strict. But now I have to finalize code that someone else started, and worked with for 3 month, and I have to finalize by tomorrow. The code base is so big so I have to pick bits and pieces to save time, and these bits and pices are clutterd bit global vars, loading of vars with requre (var are set in different files), so I just had to turn strict off to able to continue. Cheers Pär
        I know you're working on a deadline (and I'll go in to that later) but that would be the wrong way around. The absolutely very first thing I recommend when inheriting bad code (and we have all had to do it) is getting the source to compile under use strict;.

        As for the deadline (arbitrariness aside) getting code running under use strict; will take less time than you think. Really it will, no matter how bad the code is. Most importantly, it almost assuredly save you time (short term and long term).

        1. The obvious reason: You will have better code to read and debug.
        2. The not so obvious reason which will save you the most time: You will find the little tiny bugs that no one ever thinks to look for right away - instead of days, weeks, ... later when it takes you most of a day to find that one last irritating bug that everyone is getting bent out of shape about.

        Seriously, it sounds like waste of time, but it isn't.
        You may think, 'This code is worse than anyone has seen, so adding use strict will take longer than anyone can understand.' but trust me I have seen worse code and using strict is more important and saves more time, the worse the code is.



        grep
        One dead unjugged rabbit fish later
Re: deleteing array elements I get mad!!
by rminner (Chaplain) on Oct 03, 2006 at 08:06 UTC
    You should dereference your reference :)
    delete $cashflow->[$count]
    btw. where are you getting $count from?
    # delete element number $i from @array: delete $array[$i]; # delete element number $i from $array_ref; delete $array_ref->[$i];
Re: deleteing array elements I get mad!!
by Fletch (Bishop) on Oct 03, 2006 at 12:08 UTC

    Array / ref problems aside, it's generally counter-indicated by the owner's manual to muck with an array you're iterating over while you're iterating over it. Quoth perlsyn:

    If any part of LIST is an array, "foreach" will get very confused if you add or remove elements within the loop body, for example with "splice". So don't do that.
Re: deleteing array elements I get mad!!
by Jenda (Abbot) on Oct 03, 2006 at 13:22 UTC

    Apart from all those otherwise good advices there is the most important thing. You should not use delete() on arrays. It doesn't do what you most likely expect. delete() is fine on hashes, but on arrays you should use splice(). Try to run this:

    use Data::Dumper; my @a = (1,2,3); print Dumper(\@a); delete $a[1]; print Dumper(\@a);
    As you can see the 1st item in the array was not removed, but only set to undef, the number of items in the array has not changed. I do think what you want is
    @a = (1,2,3); splice @a, 1, 1; # replace 1 element(s) starting on position 1 by noth +ing print Dumper(\@a);

Re: deleteing array elements I get mad!!
by Tanktalus (Canon) on Oct 03, 2006 at 13:58 UTC

    Taking Jenda's response just a step further, rather than use splice (which still could damage the foreach's ability to loop properly, as per Fletch's comment), use grep.

    sub man_cfs { my ($cashflow, $valid_ports) = @_; @$cashflow = grep { # only keep the ones whose portfolio_id is not empty. $_->{'portfolio_id'} ne '' } @$cashflow; # loop through the new @$cashflow to do other stuff.
    There are other ways to check for the portfolio_id's truthiness. If 0 is not a valid value, you could just remove the ne '' bit, and let perl evaluate the string in boolean context. If 0 is a valid value, perhaps the portfolio_id element may not exist. In the interests of being fully strict-compliant, perhaps you want exists $_->{'portfolio_id'} and length $_->{'portfolio_id'} - that it both exists, and has a non-zero length, then keep it.

    Hope that helps,