No one has mentioned the CPAN modules that help solve this sort of problem: List::Compare, Set::Scalar, and Set::Array. If "elements in exactly 2 lists" was the only 'set' operation in a program, I would use jhourcle's second solution. If more than one 'set' operation is needed, the modules start to really pay off.
Set::Scalar has a flexible syntax, allowing for a dense mix of overloaded operators and chained methods. It can solve the OP problem quite tersely:
use strict; use warnings; my @array1 = ( 1, 2, 5, 9, 11, 12, 13 ); my @array2 = ( 1, 2, 5, 11, 12, 13 ); my @array3 = ( 1, 2, 5, 7, 9, 12, 13 ); use Set::Scalar; my $x = Set::Scalar->new(@array1); my $y = Set::Scalar->new(@array2); my $z = Set::Scalar->new(@array3); # Either way works. #my $answer = $x->union($y,$z) # - $x->unique($y,$z) # - $x->intersection($y,$z); my $answer = ($x + $y + $z) - ($x / $y / $z) - ($x * $y * $z); my @only_two = $answer->elements();
List::Compare is better for a generalized form of the OP problem (find elements occurring exactly N times in X lists). In addition to the expected set functions (get_intersection, get_symmetric_difference, etc.), it provides are_members_which, which builds a hash like this:
The number of elements in each of those value arrays is equal to the number of lists where key is found.{ 1 => [ 0, 1, 2 ], 2 => [ 0, 1, 2 ], 5 => [ 0, 1, 2 ], 7 => [ 2 ], 9 => [ 0, 2 ], 11 => [ 0, 1 ], 12 => [ 0, 1, 2 ], 13 => [ 0, 1, 2 ], }
use List::Compare; sub elements_found_in_exactly_n_lists { my ($n, @array_refs) = @_; my $lc = List::Compare->new(@array_refs); my @u = $lc->get_union(); my %h = %{ $lc->are_members_which(\@u) }; return grep { @{$h{$_}} == $n } keys %h; } my @only_two = elements_found_in_exactly_n_lists( 2, \@array1, \@array2, \@array3, );
In reply to Re: array comparison question
by Util
in thread array comparison question
by Anonymous Monk
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |