What is the best way to remove a value from an array in Perl?

There is a lot of data in the array and I need to remove two elements.

Below is a snippet of the code I'm using,

my @array = (1,2,3,4,5,5,6,5,4,9); my $element_omitted = 5; @array = grep { $_ != $element_omitted } @array; 
+69
arrays perl
Oct 06 '08 at 13:29
source share
12 answers

Use splicing if you already know the index of the item you want to remove.

Grep works if you are looking.

If you need to do a lot of them, you will get much better performance if you save your array in sorted order, since after that you can perform a binary search to find the index you need.

If this makes sense in your context, you might want to use the "magic value" for deleted records, rather than deleting them to preserve when moving data - for example, set deleted items to undef. Naturally, this has its own problems (if you need to know the number of "live" elements, you need to track them separately, etc.), But it may be a problem depending on your application.

Edit In fact, now when I take a second look - do not use the grep code above. It would be more convenient to find the index of the element you want to remove, and then use splicing to remove it (the code on which you accumulated all the inappropriate results.)

 my $index = 0; $index++ until $arr[$index] eq 'foo'; splice(@arr, $index, 1); 

This will remove the first occurrence. Removing all occurrences is very similar, except that you want to get all indexes in one pass:

 my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr; 

The rest remains as an exercise for the reader - remember that the array changes as it splices!

Edit2 John Syracuse correctly indicated that I had a mistake in my example .. fixed, sorry.

+76
Oct 06 '08 at 13:37
source share

splice will remove the array element by index. Use grep, as in your example, to search and delete.

+12
Oct 06 '08 at 13:30
source share

Is this something you are going to do a lot? If so, you can consider a different data structure. Grep is going to search the entire array every time, and for a large array it can be quite expensive. If speed is a problem, then you might want to use Hash instead.

In your example, the key will be a number, and the value will be the number of elements in that number.

+7
Oct 06 '08 at 13:47
source share

if you change

 my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr; 

to

 my @del_indexes = reverse(grep { $arr[$_] eq 'foo' } 0..$#arr); 

This avoids the problem of renumbering the array by first deleting elements from the back of the array. Insert splicing () in the foreach loop clears @arr. Relatively simple and readable ...

 foreach $item (@del_indexes) { splice (@arr,$item,1); } 
+4
Jan 07 '13 at 7:35
source share

I think your solution is the easiest and most convenient for maintenance.

The rest of the post documents the difficulty of translating element tests into splice offsets. Thus, it gives a more complete answer.

Look at the gyrations you need to go through in order to have an efficient (i.e. one-pass) algorithm to check list items for indexes. And this is not at all intuitive.

 sub array_remove ( \@& ) { my ( $arr_ref, $test_block ) = @_; my $sp_start = 0; my $sp_len = 0; for ( my $inx = 0; $inx <= $#$arr_ref; $inx++ ) { local $_ = $arr_ref->[$inx]; next unless $test_block->( $_ ); if ( $sp_len > 0 && $inx > $sp_start + $sp_len ) { splice( @$arr_ref, $sp_start, $sp_len ); $inx = $inx - $sp_len; $sp_len = 0; } $sp_start = $inx if ++$sp_len == 1; } splice( @$arr_ref, $sp_start, $sp_len ) if $sp_len > 0; return; } 
+2
Oct. 06 '08 at 15:50
source share

Delete all occurrences of "something" if array.

Based on SquareCog answer:

 my @arr = ('1','2','3','4','3','2', '3','4','3'); my @dix = grep { $arr[$_] eq '4' } 0..$#arr; my $o = 0; for (@dix) { splice(@arr, $_-$o, 1); $o++; } print join("\n", @arr); 

Each time we remove the index from @arr , the next correct index to delete will be $_-current_loop_step .

+1
Aug 10 '12 at 16:50
source share

I use:

 delete $array[$index]; 

Perldoc remove .

+1
Feb 08 '13 at 5:41
source share

You can use a non-capturing group and a list of pipe dividers for deletion.

 perl -le '@ar=(1 .. 20);@x=(8,10,3,17);$x=join("|",@x);@ar=grep{!/^(?:$x)$/o} @ar;print "@ar"' 
+1
Apr 11 '14 at 16:29
source share

The best I have found is a combination of "undef" and "grep":

 foreach $index ( @list_of_indexes_to_be_skiped ) { undef($array[$index]); } @array = grep { defined($_) } @array; 

This is a trick! Federico

+1
Apr 19 '16 at 12:52 on
source share

If you know the index of the array, you can remove () it. The difference between splice () and delete () is that delete () does not renumber the remaining elements of the array.

0
Oct 06 '08 at 13:41
source share

Similar code I once wrote to remove lines not starting with SB.1 from an array of strings

 my @adoSymbols=('SB.1000','RT.10000','PC.10000'); ##Remove items from an array from backward for(my $i=$#adoSymbols;$i>=0;$i--) { unless ($adoSymbols[$i] =~ m/^SB\.1/) {splice(@adoSymbols,$i,1);} } 
0
Mar 13 '13 at 9:13
source share

You can use array splitting instead of splicing. Grep to return the indexes you want to keep and use slicing:

 my @arr = ...; my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr; @arr = @arr[@indiciesToKeep]; 
0
Nov 20 '17 at 19:34 on
source share



All Articles