Somnath Paul
Somnath Paul

Reputation: 190

Perl Deleting element from array

I have a 2D array in perl. I want to delete all elements which has the pattern <<< or >>>. I have written a perl code, it works good till matching pattern, however it cannot delete that element, some error occurs.

 foreach my $x(@array)
   { 
     foreach my $y(@$x)
     {
        if($y =~ (m/^(\<+)|(\>+)$/ig))
         {
            delete $y;
         }
     }
   }

Can you help me to delete that particular element that matches the pattern. (I want to delete and remove from array, not undef it)

Upvotes: 2

Views: 7359

Answers (4)

David W.
David W.

Reputation: 107090

Let's say your array looks like this:

1  2  3  4
5  X  6  7
8  9  A  B

You want to delete X. What do you want to happen? What should your new array look like after the delete?

Do you wan this:

1  2  3  4
4  6  7
8  9  A  B

Or this?

1  2  3  4
5  9  6  7
8     A  B

That's the first thing you need to decide. Second, you can't use delete. The delete command deletes a keyed value from a hash and not an array. If you have an array like this:

my @array = qw(0 1 2 3 4 5 X 7 8 9);

And you want to delete the X (which is $array[6]), you'd use the splice command:

splice @array, 6, 1;

Finally, Perl does not have 2 dimensional arrays, so you can't delete a value from a 2 dimensional array.

What you have is an array of references to a second array. Think of it this way:

my @row0 = qw(1 2 3 4);
my @row1 = qw(5 X 6 7);
my @row2 = qw(8 9 A B);

my @two_d_array = (\@row0, \@row1, \@row2);

Or, I could do this by column:

my @col0 = qw(1 5 8);
my @col1 = qw(2 X 6);
my @col2 = qw(2 6 A);
my @col3 = qw(4 7 B);

my @two_d_array = (\@col0, \@col1, \@col2, \@col3);

When you talk about.

if ( $two_d_array[1][1]` eq "X" ) {

What is going on is that Perl is messing with your mind. It is making you think there's a two dimensional array is involved, but it's not really there.

A more accurate way of writing this would be:

if ( ${ $two_d_array[1] }[1] eq "X" ) {

or, more cleanly:

if ( $two_d_array[1]->[1] eq "X" ) {

So first, decide what you mean by deleting a value. In a two dimensional array, if you actually delete that value, you end up ruining the dimensional structure of that array. Maybe you can replace the value at that point with an undef.

Once you do that, you must understand what you're actually dealing with: An array of references to arrays.

for my $array_reference ( @two_d_array ) {
    for my $value ( @{ $array_reference } ) {
        if ( $value =~ /^(<+|>+)$/ ) {
            $value = undef;   #See Note #1
        }
    }
}

Note #1: When you use a for loop, the index of the array is a link to the actual value in the array. Therefore, when you change the index, you're changing the actual value. That's why this will work.

If you really, really want to delete the element using splice, you will have to decide if you want your elements moving up to replace the deleted value or moving to the left to replace the deleted value. If you want the values to the moving left, you want an array or references to row arrays. If you wan the values moving up to fill in the deleted value, you want an array of reference to column arrays.

Remember that computers will do exactly what you tell them to do and not what you want them to do. Make sure you understand exactly what you want.

Upvotes: 4

TLP
TLP

Reputation: 67930

You are applying delete on a scalar value, $y, and delete is only meant to be applied to hashes and arrays. You would need to do do

for my $x (0 .. $#array) {
    for my $y (0 .. $#{$array[$x]}) {
        if (...) { delete $array[$x][$y]; }

The best solution, in my opinion, is to remove the value before storing it in the array. I am guessing you read it in from some data source such as a file, and that would be the best place to filter it out. E.g.

while (<$fh>) {
    ....
    @values = grep !/^[<>]+/, @values;    # filtering
    push @array, \@values;                # storing
}

On that note, you can also do it afterwards, of course, with something like:

for (@array) {
    @$_ = grep !/^[<>]+/, @$_;
}

Upvotes: 2

perreal
perreal

Reputation: 98118

delete does not alter array indices so it is not what you want. If you want to delete elements by value, use something like this:

foreach my $x(@array)
   {   
       $x = [ grep { $_ !~ (m/^(\<+)|(\>+)$/ig)} @$x ];
       print join(",", @$x), "\n";
   }   

or, use splice. But then you will need to iterate the array using indices rather than values.

Also see Perl-delete, Perl-splice.

Upvotes: 0

Suic
Suic

Reputation: 2501

You can delete elements from arrays, by splice function: splice(@array, $index, 1); 1 in this example is number of elements, you want to delete
delete function only sets array value to undef

Upvotes: 1

Related Questions