I have two sets,
@ a=(2,5,3)@ B=(3,4,1), I want to get (2,5,4,1)
Whether the output is a newline, a space, or a comma is not important
My idea is:
Intersection @ c=(2,5,3,4,1), union @ d=(3), result: @ c - @ d=(2,5,4,1)
Is there a more elegant solution (I don't like to install additional modules)
CodePudding user response:
Using a hash is the easiest way to represent a set (the keys are the members of the set, and the value can mean anything; often undef
is used). Here's some code (could be more compact):
@a = (2,5,3);
@b = (3,4,1);
foreach $val ( @a ) { $d{$val} |= 1 };
foreach $val ( @b ) { $d{$val} |= 2 };
foreach $val ( keys(%d) ) {
push(@result, $val) if $d{$val} != 3;
}
print(join(",", @result));
We are using two bits in the values of %d
to represent membership in each set, and skipping values that are in both sets.
CodePudding user response:
While I duly note the question's premise
I don't like to install additional modules
I would still like to demonstrate the utility of libraries. With Set::Scalar
use warnings;
use strict;
use feature 'say';
use Set::Scalar;
my @ary1 = (2,5,3);
my @ary2 = (3,4,1);
# "want to get (2,5,4,1)"; like union - intersection
my $s1 = Set::Scalar->new(@ary1);
my $s2 = Set::Scalar->new(@ary2);
my $u = $s1->union($s2);
my $i = $s1->intersection($s2);
my $res = $u - $i;
say $res;
# Or just
my $symm_diff = $s1->symmetric_difference($s2);
say $symm_diff;
Prints a (1 2 4 5)
line, twice.
This particular set operation is called symmetric difference, and the library has it as well as shown at the end. There is a whole lot more that this library can do; this is just basic stuff.
There is also the related Set::Object, with a different slant, but also with all methods as above (including the symmetric_difference
) for directly answering this need.
Out of list-manipulation libraries I'd mention List::Compare, which also directly resolves this question and has a lot more.
CodePudding user response:
my %s;
$s{ $_ } |= 1 for @a; # Sets bit 0 for elements of set @a
$s{ $_ } |= 2 for @b; # Sets bit 1 for elements of set @b
my @union = keys %s; # One or two bits set
my @isect = grep { $s{ $_ } == 3 } keys %s; # Both bits set
my @symdiff = grep { $s{ $_ } != 3 } keys %s; # Only one bit set