5

I use the following code to exclude elements in @{$x} at indexes in @{$index}. But I am not sure it is the most efficient way to implement this function. Does anybody have any better way to do.

sub arrayexclude {
    my $x = shift;
    my $index = shift;

    my @keep_index = (0 .. (scalar(@{$x})-1));

    delete @keep_index[@{$index}];

    my $result=[];

    for my $i (@keep_index) {
        if(defined $i) {
            push @{$result}, @{$x}[$i];
        }
    }
    return $result;
}
user1424739
  • 11,937
  • 17
  • 63
  • 152
  • 1
    As a more of a side note, `0 .. (scalar(@{$x})-1)` is much more easily written `0 .. $#$x`, or if you prefer `0 .. $#{$x}`. – hobbs Mar 15 '16 at 05:02
  • `$#foo` is often misused, and if you're adding 1 to it, you're doing something wrong... but if you're ranging over the indices of an array, or talking about the last index, it's exactly the thing you want. – hobbs Mar 15 '16 at 05:05

3 Answers3

2

You don't need anything beyond an array slice, so I'd avoid creating a sub just for this.

my @wanted = @array[@indices];

If you're working with array references, the same recipe applies:

my @wanted = @{$array}[@$indices];
Zaid
  • 36,680
  • 16
  • 86
  • 155
1

Prepare a hash so to identify indices efficiently, then index into the original array with it.

my %ref_index;
@ref_index{ @ind_toss } = ();    
@arr_filt = @arr_orig[ grep { !exists $ref_index{$_} } (0..$#arr_orig) ];

Final @arr_filt contains elements of the @arr_orig at indices other than those in @ind_toss.

See solution by ysth in this post for filtering array elements by another array in general.


Wrap it in a sub and run. Array with indices to exclude is @ind_toss, original array is @arr_orig.

use warnings;
use strict;

my @ind_toss = (1, 4, 5);
my @arr_orig = ('a', '1', 'b', 'c', '2', '6', 'd', 'e');

my @filtered = @{ filter_array_by_index(\@arr_orig, \@ind_toss) };

print "@filtered" . "\n";    

sub filter_array_by_index {
    my ($rarr, $rind) = @_;    
    my %ref_index;
    @ref_index{ @$rind } = (); 
    return [ @$rarr[grep { !exists $ref_index{$_} } (0..$#$rarr)] ];
}

Prints

a b c d e

Notes

The return from the sub, as hinted in comment by Oleg V. Volkov, can also be written as

return [ map { !exists $ref_index{$_} ? $rarr->[$_] : ()  } (0..$#$rarr) ];

This avoids construction of a list by grep and a slice but rather indexes into the array conditionally.

Community
  • 1
  • 1
zdim
  • 64,580
  • 5
  • 52
  • 81
0

Just to clean up a bit on zdim's answer (which is correct anyway):

sub filter_array_by_index {
    my ( $rarr, $rind ) = @_;
    my %ref_index = map { $_ => 1 } @ind_toss;
    my @indices = grep { !$ref_index{$_} } (0..$#$rarr);

    return @{$rarr}[@indices];
}
ChatterOne
  • 3,381
  • 1
  • 18
  • 24