Reputation: 883
i am trying to create a subroutine that does the following :
Example :
@a = ( 1, 2, 3, 4, 5 );
@b = ( 1, 2, 3, 4, 5, 6, 7);
Expected output : @c = ( 6, 7 );
Called as : filter_list(@filter, @base)
###############################################
sub filter_list {
my @names = shift;
my @arrayout;
foreach my $element (@_)
{
if (!($element ~~ @names )){
push @arrayout, $element;
}
}
return @arrayout
}
Test Run :
@filter = ( 'Tom', 'John' );
@array = ( 'Tom', 'John', 'Mary' );
@array3 = filter_list(@filter,@array);
print @array3;
print "\n";
Result :
JohnJohnMary
Can anyone help? Thank you.
Upvotes: 1
Views: 2256
Reputation: 66883
This uses List::Compare, a module with a large collection of routines for comparing lists.
Here you want get_complement
use warnings;
use strict;
use List::Compare;
my @arr1 = ( 1, 2, 3, 4, 5 );
my @arr2 = ( 1, 2, 3, 4, 5, 6, 7);
my $lc = List::Compare->new(\@arr1, \@arr2);
my @only_in_second = $lc->get_complement;
print "@only_in_second\n";
The module has many options.
If you don't need the result sorted, pass -u
to the constructor for faster operation.
There is also the "Accelerated Mode", obtained by passing -a
. For the purpose of efficient repeated comparisons between the same arrays many things are precomputed at construction. With this flag that is suppressed, which speeds up single comparisons. See List::Compare Modes.
These two options can be combined, List::Compare->new('-u', '-a', \@a1, \@a2)
.
Operations on three or more lists are supported.
There is also the functional interface, as a separate List::Compare::Functional module.
Upvotes: 1
Reputation: 385764
You can't pass arrays to subs, only scalars. So when you do
my @filtered = filter_list(@filter, @base);
you are really doing
my @filtered = filter_list($filter[0], $filter[1], ..., $base[0], $base[1], ...);
As such, when you do
my @names = shift;
you are really doing
my @names = $filter[0];
which is obviously wrong.
The simplest solution is to pass references to the arrays.
my @filtered = filter_list(\@filter, \@base);
A hash permits an efficient implementation (O(N+M)).
sub filter_list {
my ($filter, $base) = @_;
my %filter = map { $_ => 1 } @$filter;
return grep { !$filter{$_} } @$base;
}
Alternatively,
my @filtered = filter_list(\@filter, @base);
could be implemented as
sub filter_list {
my $filter = shift;
my %filter = map { $_ => 1 } @$filter;
return grep { !$filter{$_} } @_;
}
Upvotes: 5
Reputation: 164809
What you're looking for is the difference of two sets. This, along with union, intersection, and a bunch of others are set operations. Rather than writing your own, there's plenty of modules for dealing with sets.
Set::Object is very fast and featureful. I'd avoid using the operator interface (ie. $set1 - $set2) as it makes the code confusing. Instead use explicit method calls.
use strict;
use warnings;
use v5.10;
use Set::Object qw(set);
my $set1 = set(1, 2, 3, 4, 5);
my $set2 = set(1, 2, 3, 4, 5, 6, 7);
say join ", ", $set2->difference($set1)->members;
Note that sets are unordered and cannot contain duplicates. This may or may not be what you want.
Upvotes: 1