Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Removing Items from an Array

by ibanix (Hermit)
on Dec 03, 2002 at 21:21 UTC ( [id://217348]=perlquestion: print w/replies, xml ) Need Help??

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

Greetings Monks!

I have a piece of code that looks something like:
my @array; foreach my $item (@array) { my $return = foo($item); if (!$return) { print "$item returned false\n"; next; } other_stuff(); }
I'd like to remove $item from @array inside the  if (!$return) block.

I suppose I could do:
my @array; my @newarray; foreach my $item (@array) { my $return = foo($item); if (!$return) { print "$item returned false\n"; push @newarray, $item; next; } other_stuff(); } @array = @newarray; # or whatever...
but that seems a waste, as I have to declare another array. Is there a simple way to remove a given $item from an @array?

Thanks!

<-> In general, we find that those who disparage a given operating system, language, or philosophy have never had to use it in practice. <->

Replies are listed 'Best First'.
Re: Removing Items from an Array
by BrowserUk (Patriarch) on Dec 03, 2002 at 21:59 UTC

    In most cases, splice is the simplest way of removing an item from the middle of an array but it is a dodgey practice to modify an array that you are iterating over using a for/foreach loop. Changing the array whilst you are halfway through it is not a good idea.

    The easiest way to do what you want is to set the elements that are to be removed to undef in the loop and then use grep to remove them at the end.

    #! perl -slw use strict; my @array = (1 .. 100); print scalar @array; for my $i (0 .. $#array) { $array[$i] = undef if $array[$i] % 10 == 0; } print scalar @array; @array = grep{ $_ } @array; print scalar @array;

    It is safe to remove elements from the top of the array whilst if you process the array in reverse order.

    #! perl -slw use strict; my @array = (1 .. 100); print scalar @array; my @toDelete; for my $i (reverse 0 .. $#array) { splice(@array, $i, 1) if $array[$i] % 10 == 0; } print scalar @array;

    You can also accumulate an array of indexes for the elements to be removed and use delete on an array slice built from the index array. Unfortunately, delete will only remove the element from the array completely of it happens to be the last element. Otherwise it just sets it to be undef and you still need to use grep to trim the rest out, so there is no advantage with this over the first method above.


    Okay you lot, get your wings on the left, halos on the right. It's one size fits all, and "No!", you can't have a different color.
    Pick up your cloud down the end and "Yes" if you get allocated a grey one they are a bit damp under foot, but someone has to get them.
    Get used to the wings fast cos its an 8 hour day...unless the Govenor calls for a cyclone or hurricane, in which case 16 hour shifts are mandatory.
    Just be grateful that you arrived just as the tornado season finished. Them buggers are real work.

Re: Removing Items from an Array
by sauoq (Abbot) on Dec 03, 2002 at 22:06 UTC

    Use splice to remove elements from an array.

    for (0..$#array) { if (foo($array[$_])) { splice @array, $_, 0; # remove it. } else { print "$array[$_] returned false\n"; next; } other_stuff(); }

    Actually, don't use that code. Why? Because your loop will soon become out of sync with the array. You can still use splice but you have to keep your index in sync while you iterate and remove elements.

    my $i = 0; for my $item (@array) { if (foo($item)) { splice @array, $i, 0; # remove it. } else { print "$item returned false\n"; $i++; next; } other_stuff(); }
    -sauoq
    "My two cents aren't worth a dime.";
    
Re: Removing Items from an Array
by kabel (Chaplain) on Dec 03, 2002 at 21:33 UTC
    F:\>perl use strict; use warnings; my @numbers = 1 .. 10; @numbers = grep { $_ > 4 } @numbers; print "@numbers"; ^D 5 6 7 8 9 10 F:\>
    grep iterates through @numbers, sets $_ to the current element and if the block returns true, the element will be in the output list. so just call foo inside the grep block with $_ and it should work.
      It would look like @array = grep { foo($item) } @array; would generate my correct new list; but what about printing out when it returns false, or a larger set of things I need to do upon a false return?

      <-> In general, we find that those who disparage a given operating system, language, or philosophy have never had to use it in practice. <->
        Try
        my @arraynew = grep { foo($item) } @array; if ($#arraynew != $#array) { # do something when item removed }
        or
        my @arraydelete = grep { !foo($item) } @array; @array = grep { foo($item) } @array; foreach my $item (@arraydelete ) { # do something when item removed }
        Hope it helps
        UnderMine
        but what about printing out when it returns false, or a larger set of things I need to do upon a false return?

        Well if it's just a matter of printing a message when foo() returns false, you could just add a print inside sub foo { ... } -- but if it's really "a larger set of things" that are needed on a false return, it might be too much to cram into sub foo { ... } (or maybe you don't have the privelege to muck with what "foo()" does).

        In which case, you could try defining a second sub, let's call it "foo_report()", have it do whatever is needed when foo() returns false, and make sure that foo_report() also returns false -- and include it in the grep condition:

        @array1 = qw/7 2 5 8 0 10/; @array2 = grep { foo($_) or foo_report($_) } @array1; print join " ","array2 contains:",@array2,$/; sub foo { ( $_[0] / 2 > 1 ) } sub foo_report { print "foo returned false for $_[0]\n"; # do lots of other stuff... 0; # make sure to return false, for grep's sake } __OUTPUT__ foo returned false for 2 foo returned false for 0 array2 contains: 7 5 8 10
Re: Removing Items from an Array
by Mr. Muskrat (Canon) on Dec 03, 2002 at 21:45 UTC

    @array = grep { foo($_) } @array;

    Update: If you want @array to contain only the items that return true, then this is fine. If you want to do some things if true and some other things if false, then stick with your original code.

Re: Removing Items from an Array
by ibanix (Hermit) on Dec 03, 2002 at 21:35 UTC
    Oops, I goofed. The push @newarray, $item; should be outside the if (!$return) block.

    <-> In general, we find that those who disparage a given operating system, language, or philosophy have never had to use it in practice. <->

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://217348]
Approved by krisahoch
Front-paged by tye
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (7)
As of 2024-04-19 10:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found