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

I want to remove specific elements from an array by index. I've come up with two solutions to this problem the first seems better able to deal with garbage data, the second is more efficient. Which method would you use for this problem. Something I haven't thought of?

The real data will be a list of integers corresponding to line numbers in a text file(starting at 0). The list will be returned via a cgi.pm multiple-value-enabled scolling_list().

#!/usr/bin/perl -w use strict; &delete_entry(5,3,0); &splice_entry(5,3,0); sub delete_entry{ my @removes = @_; my $datafile = "shows.data"; open(GETDEL,$datafile)or die "Cannot open $datafile: $!"; my @rows = <GETDEL>; close(GETDEL); my @fulllist; for (my $index =0;$index < @rows ;$index++) { push @fulllist,$index,$rows[$index]; } my %fullhash =(@fulllist); for (@removes) { delete $fullhash{"$_"}; } my @keeplist = (values %fullhash); print @keeplist; } sub splice_entry{ my @kills = sort @_; my $datafile = "shows.data"; open(GETSPL,$datafile)or die "Cannot open $datafile: $!"; my @rows = <GETSPL>; close(GETSPL); my $decr = 0; for(@kills){ splice(@rows,$_+$decr,1); --$decr; } print @rows; }

Replies are listed 'Best First'.
Re: remove multiple indexes from an array
by merlyn (Sage) on Dec 18, 2001 at 20:22 UTC
    You need to sort numerically on that second one:
    sub splice_entry_2 { my @kills = sort { $a <=> $b } @_; local *ARGV; @ARGV = "shows.data"; while (<>) { if (@kills and $. >= $kills[0]) { shift @kills while @kills and $. >= $kills[0]; next; } print; } }

    -- Randal L. Schwartz, Perl hacker

      Yikes!
      $ perl -e '@a =(1..20);for(sort @a){print "$_ "}' 1 10 11 12 13 14 15 16 17 18 19 2 20 3 4 5 6 7 8 9
      Thanks, Randal. That was a disaster waiting to happen.
Re: remove multiple indexes from an array
by strat (Canon) on Dec 18, 2001 at 20:09 UTC
    Does
    my %fullhash = (@fulllist);
    really work? I'd rather use:
    my %fullhash = (); @fullhash(@fulllist) = (); my @fulllist = keys %fullhash; # maybe sort keys
    if the listorder doesn't matter

    Best regards,
    perl -e "print a|r,p|d=>b|p=>chr 3**2 .7=>t and t"

      Hashes can be constructed from lists. In fact, that's what the following is doing:
      my %hash = ( a => 1, b => 2, );
      => is just a comma that quotes the word just to its left, if it's unquoted. :-)

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        Gotcha!

        Run the following from a file. Run it from an eval.

        use strict; my @strange = (explain => "this"); print @strange;
        Yes, this is a bug. Fixed in bleadperl. :-)
Re: remove multiple indexes from an array
by dragonchild (Archbishop) on Dec 18, 2001 at 20:10 UTC
    Your first is better, but doesn't work as written. Try something like:
    my $i = 0; my %fullHash = map { ($i++, $_) } @rows; # Delete your stuff here my @keeplist = map { $fullhash{$_} } sort { $a <=> $b } keys %fullHash +;
    The problem was that you were reconstituting the array in the random order of the hash. Hashes don't preserve order, so you may need to sort if you're going from hash => array.

    Update: Modified the sort from ASCIIbetically to numerically. Whoops! :-)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Re: remove multiple indexes from an array
by jmcnamara (Monsignor) on Dec 18, 2001 at 20:59 UTC

    The hash example seems a little convoluted. So I guess that the splice example is better.

    Alternatively it might be better to drop the unwanted lines when you are reading the data:

    sub new_splice { my @kills = @_; my $datafile = "shows.data"; open(GETSPL, $datafile) or die "Cannot open $datafile: $!"; my @rows; while (<GETSPL>) { # Only store the lines not listed in @kills push @rows, $_ unless grep {$_ == $. -1} @kills; } close(GETSPL); print @rows; }

    --
    John.

Re: remove multiple indexes from an array
by Zaxo (Archbishop) on Dec 18, 2001 at 23:26 UTC

    It can be simpler.

    #!/usr/bin/perl -w use strict; my $datafile = "/path/to/shows.data"; open GETSPL, "< $datafile" or die "Cannot open $datafile: $!"; my @rows = <GETSPL>; close(GETSPL) or die $!; delete @rows[5,3,0]; print grep {defined} @rows;
    delete can take a slice, works fine. We use grep to filter out the empty slots.

    After Compline,
    Zaxo

Re: remove multiple indexes from an array
by hotshot (Prior) on Dec 18, 2001 at 20:24 UTC
    It looks like the second approach is better, coz' you don't need to define a hash and then take it's values and print them, it depends on hoe much you need good performances.

    If the order of the rows in the array doesn't matter to you, I can suggest that after reading the array, loop on the indexes to be deleted and move each time the last array entry to to the place to be deleted (and of course pop it): just make sure that the last index of @rows is not an index you want to delete. I think this is better if you care for performances (but only if the order is not relevant, as I said)

    Hotshot
Re: remove multiple indexes from an array
by thunders (Priest) on Dec 18, 2001 at 20:28 UTC
    Both methods worked. I tested each and they did what I expected. the hash uses the array index as a key and the array value as a value so no real worry about order, i can sort by key as I print. and my %fullhash =(@fulllist); is a somewhat murky but still valid way to assign a hash. When the deletes are complete the plan is to overwrite the original file, which is used as a data source for another cgi script.