Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Removing array elements

by sandrider (Acolyte)
on Aug 24, 2005 at 00:18 UTC ( #486050=perlquestion: print w/replies, xml ) Need Help??

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

Hi All,

I was told that I shouldn't use the C format of using for (i.e. for ($i=0;$i<10;$i++) for replacing the array's elements but use perl's for/foreach command. That works fine but I need to remove some cells/elements from an array, how do it using for/foreach?

my current code is like this:

foreach my $i (@a) { foreach my $j (@b) { if ($i eq $j) { undef $i; undef $j; last; } } }

I can't use delete and undef just removes the contents leaving my array with lots of empty 'spaces'. How can I remove the array's cell totally and not just the contents?

Thanks.

Desmond

Replies are listed 'Best First'.
Re: Removing array elements
by Zaxo (Archbishop) on Aug 24, 2005 at 00:31 UTC

    You want the splice operator.

    Incidentally, to get away with removing elements from an array in a for loop, you need to take them in reverse order:

    for (reverse 0 .. $#a) { splice @a, $_, 1 if condition($a[$_]); }
    Many of us would write that with grep: @a = grep { ! condition($_) } @a;

    You appear to be taking the set differences of two arrays. That has a well-known technique in perl:

    my (%a, %b); @a{@a} = (); @b{@b} = (); delete @a{@b}; delete @b{@a}; @a = keys %a; @b = keys %b; # or if order matters, # @a = grep {exists $a{$_}} @a; # @b = grep {exists $b{$_}} @b;
    The funny-looking @a{@a} constructions are hash slices.

    After Compline,
    Zaxo

      While splice is the way to remove elements from an array, nb 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.

      In this situation I usually (as others have suggested) use a C-style for loop.

        Hence reverse. If you remove or insert elements from the end, the positions of unvisited elements are not affected.

        After Compline,
        Zaxo

Re: Removing array elements
by pg (Canon) on Aug 24, 2005 at 01:17 UTC

    If deletion is so often, you probably should use hash instead of array. Here is some benchmark:

    use strict; use warnings; use Benchmark("timethese"); my $array; for (1 .. 100_000) { push @$array, $_; } my $hash; for (1 .. 100_000) { $hash->{$_} = 1; } timethese(1, {'array' => sub {s1($array)}, 'hash' => sub{s2($hash)}}); sub s1 { my $array = shift; for (my $i = 100_000; $i > 0; $i --) { splice(@$array, int(rand($i)), 1); } } sub s2 { my $hash = shift; for (my $i = 100_000; $i > 0; $i --) { delete($hash->{int(rand($i))}); } }

    Result on my XP is:

    Benchmark: timing 1 iterations of array, hash... array: 8 wallclock secs ( 7.38 usr + 0.00 sys = 7.38 CPU) @ 0 +.14/s (n=1 ) (warning: too few iterations for a reliable count) hash: 0 wallclock secs ( 0.20 usr + 0.00 sys = 0.20 CPU) @ 4 +.93/s (n=1 ) (warning: too few iterations for a reliable count) C:\Perl\bin>perl -w math1.pl Benchmark: timing 1 iterations of array, hash... array: 8 wallclock secs ( 7.44 usr + 0.00 sys = 7.44 CPU) @ 0 +.13/s (n=1 ) (warning: too few iterations for a reliable count) hash: 0 wallclock secs ( 0.25 usr + 0.00 sys = 0.25 CPU) @ 4 +.00/s (n=1 ) (warning: too few iterations for a reliable count)
Re: Removing array elements
by GrandFather (Sage) on Aug 24, 2005 at 00:24 UTC

    Use splice


    Perl is Huffman encoded by design.
Re: Removing array elements
by rjbs (Pilgrim) on Aug 24, 2005 at 00:40 UTC

    (Update: the above solution using reverse is cooler and better than this one.

    I would suggest using splice rather than delete for this purpose, but either way you're going to have to use something other than for over the array.

    Like a lot of things the "three-part" or "C-style" for loop should be avoided in general, but used when needed. Don't feel that you must never use it.

    my @foo = (1,2,2,1) x 5; my @bar = (2) x 20; for (my $i = 0; $i < @foo; $i++) { print "$i\n"; if ($foo[$i] % $bar[$i]) { splice @foo, $i, 1; $i-- } } print "@foo\n";
    Because you're removing the element at $i when you use splice, you have to decrement it so that the next time you find the element that used to be at $i + 1. The splice will change the size of @foo, so the comparison in the for loop will refer to the current list size.
    rjbs
Re: Removing array elements
by injunjoel (Priest) on Aug 24, 2005 at 06:29 UTC
    Greetings all,
    Seeing as from your post you are open to learning some of the Perl specific syntactic magic that exists
    I was told that I shouldn't use the C format of using for (i.e. for ($i=0;$i<10;$i++) for replacing the array's elements but use perl's for/foreach command. That works fine...

    After giving this some thought my primary suggestion would be to take advantage of perl hashes and do something like the following
    use strict; my @a = (1..20); my @b = (2,3,13,14,17); @a = do{my %i; undef @i{@a}; delete @i{@b}; sort {$a<=>$b} keys %i};



    -InjunJoel
    "I do not feel obliged to believe that the same God who endowed us with sense, reason and intellect has intended us to forego their use." -Galileo
Re: Removing array elements
by sandrider (Acolyte) on Aug 25, 2005 at 05:29 UTC
    Thanks guys for all your help.
Re: Removing array elements
by (anonymized user) (Curate) on Aug 25, 2005 at 14:26 UTC
    Or isn't it simply:
    my %result=(); for ( @a, @b ) { if ( $result{ $_ } ) { delete $result{ $_ }; } else { $result{ $_ } = 1; } }
    The required list is now found in ( keys %result )

    One world, one people

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (2)
As of 2019-08-23 23:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?