iza has asked for the wisdom of the Perl Monks concerning the following question:
hello monks :)
it's waaay too long i haven't been coding (at all), and especially coding in perl, so please forgive me for this dumb dumb dumb question. I haven't found it answered, so either I can't even properly search or it's so dumb nobody ever dared to ask.
my need : I've got two arrays, say :
my @source=(1, 2, 3, 4, 5, 7);
my @target=(0, 1, 3, 4, 6);
and what I want is :
@values_in_target_not_in_source would be (6, 0)
@values_in_source_not_in_target would be (2, 5, 7)
of course I can see plenty of solutions requiring to go throught the lists multiple times
I wish there was some kind of "elegant and yet efficient" (ok : perlish) solution
any idea ?
this is quite close but then I'd need to go throught one of the lists one more time :-/
Re: compare arrays, and list the difference
by choroba (Cardinal) on Oct 03, 2012 at 10:48 UTC
|
my %s; undef @s{@source};
my %t; undef @t{@target};
my %tns = %t; delete @tns{@source};
my %snt = %s; delete @snt{@target};
my @tns = keys %tns;
my @snt = keys %snt;
| [reply] [d/l] |
Re: compare arrays, and list the difference
by BrowserUk (Patriarch) on Oct 03, 2012 at 11:03 UTC
|
#! perl -slw
use strict;
my @source=(1, 2, 3, 4, 5, 7);
my @target=(0, 1, 3, 4, 6);
my( $src, $tgt ) = ( '', '' );
vec( $src, $_, 1 ) = 1 for @source;
vec( $tgt, $_, 1 ) = 1 for @target;
my @srcNotTgt = grep !vec( $tgt, $_, 1 ), @source;
my @tgtNotSrc = grep !vec( $src, $_, 1 ), @target;
print "srcNotTgt:@srcNotTgt";
print "tgtNotSrc:@tgtNotSrc";
__END__
C:\test>997024
srcNotTgt:2 5 7
tgtNotSrc:0 6
The same logic works for arbitrary data using hashes instead of bitstrings.
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
RIP Neil Armstrong
| [reply] [d/l] |
Re: compare arrays, and list the difference
by Lotus1 (Vicar) on Oct 03, 2012 at 13:12 UTC
|
@Llist = qw(abel abel baker camera delta edward fargo golfer);
@Rlist = qw(baker camera delta delta edward fargo golfer hilton);
$lc = List::Compare->new(\@Llist, \@Rlist);
get_unique()
Get those items which appear (at least once) only in the first list.
@Lonly = $lc->get_unique;
@Lonly = $lc->get_Lonly; # alias
get_complement()
Get those items which appear (at least once) only in the second list.
@Ronly = $lc->get_complement;
@Ronly = $lc->get_Ronly; # alias
| [reply] [d/l] |
Re: compare arrays, and list the difference
by roboticus (Chancellor) on Oct 03, 2012 at 11:08 UTC
|
lza:
You can't have looked very hard for the answer, as it's a commonly-asked question. Read perldoc perlfaq4.
Update: I was probably expecting a bit much in thinking that beginning programmers would be able to adapt the code in perlfaq4 "How do I compute the difference of two arrays? How do I compute the intersection of two arrays". As pennance, here's a simple adaptation:
#!/usr/bin/perl
use 5.14.0;
use warnings;
use autodie;
my @source=(1, 2, 3, 4, 5, 7);
my @target=(0, 1, 3, 4, 6);
my %count = ();
$count{$_} = 1 for @source;
$count{$_} |= 2 for @target;
my (@not_in_src, @not_in_tgt, @in_both);
for (sort keys %count) {
push @not_in_tgt, $_ if $count{$_} == 1;
push @not_in_src, $_ if $count{$_} == 2;
push @in_both, $_ if $count{$_} == 3;
}
print "Not in source: ", join(", ", @not_in_src), "\n";
print "Not in target: ", join(", ", @not_in_tgt), "\n";
print "In both: ", join(", ", @in_both), "\n";
...roboticus
When your only tool is a hammer, all problems look like your thumb. | [reply] [d/l] [select] |
|
| [reply] |
|
BrowserUk:
It seems that "How do I compute the difference of two arrays? How do I compute the intersection of two arrays?" gives the essential information. Perhaps my expectation that it's a simple step from the FAQ to this question is a bit much?
In any case, since it's not an exact match to the question, I shouldn't have said that it's a FAQ.
...roboticus
When your only tool is a hammer, all problems look like your thumb.
| [reply] |
|
it's the faq I was referring to in my question
I didn't know the $count{$_} |= 2 for @target; trick - I like it, thanks !
| [reply] [d/l] |
Re: compare arrays, and list the difference
by daxim (Curate) on Oct 03, 2012 at 10:42 UTC
|
These are set operations, see Set::Scalar or similar module. | [reply] |
Re: compare arrays, and list the difference
by AnomalousMonk (Archbishop) on Oct 03, 2012 at 14:09 UTC
|
Yet another approach, not necessarily better:
>perl -wMstrict -le
"use List::MoreUtils qw(part);
use Data::Dump qw(pp);
;;
my @source = (1, 2, 3, 4, 5, 7);
my @target = (0, 1, 3, 4, 6);
;;
my %diff;
++$diff{$_} for @source;
--$diff{$_} for @target;
;;
my ($ar_in_t_not_s, undef, $ar_in_s_not_t) =
part { 1 + $diff{$_} } keys %diff;
;;
print 'in t not s: ', pp $ar_in_t_not_s;
print 'in s not t: ', pp $ar_in_s_not_t;
"
in t not s: [6, 0]
in s not t: [7, 2, 5]
| [reply] [d/l] |
Re: compare arrays, and list the difference
by Anonymous Monk on Oct 03, 2012 at 10:51 UTC
|
#!/usr/bin/perl --
use strict; use warnings; use Data::Dump;
my @source=(1, 2, 3, 4, 5, 7);
my @target=(0, 1, 3, 4, 6);
my (@union, @intersection, @difference);
my %count = ();
foreach my $element (@source, @target) {
$count{$element}++
}
foreach my $element (keys %count) {
push @union, $element;
push @{ $count{$element} > 1 ? \@intersection : \@difference }, $e
+lement;
}
dd \@source, \@target;
dd \@union;
dd \@intersection;
dd \@difference;
my @values_in_target_not_in_source = do {
my %fuf;
@fuf{ @union } = undef;
delete @fuf{ @target };
keys %fuf;
};
my @values_in_source_not_in_target = do {
my %fuf;
@fuf{ @union } = undef;
delete @fuf{ @source };
keys %fuf;
};
dd \@values_in_target_not_in_source , \@values_in_source_not_in_target
+ ;
__END__
([1 .. 5, 7], [0, 1, 3, 4, 6])
[6, 4, 1, 0, 3, 7, 2, 5]
[4, 1, 3]
[6, 0, 7, 2, 5]
([7, 2, 5], [6, 0])
| [reply] [d/l] |
Re: compare arrays, and list the difference
by Anonymous Monk on Oct 03, 2012 at 12:53 UTC
|
If you can afford to spend the memory, then a hash is a great way to find duplicates. Put the keys in the hash (with a dummy value) as you go and use exists() to check for the presence of the key. If the key exists, the value has been seen before. Be careful not to test for truth; zero is a key that exists, but it is false. | [reply] |
Re: compare arrays, and list the difference
by Anonymous Monk on Oct 03, 2012 at 16:37 UTC
|
Yup... no matter what you want to do, there's already three modules out there in CPAN that do it. :-} | [reply] |
|
definitely - as usual, in perl, tmtowtdi :-)
| [reply] |
|
|