Your skill will accomplishwhat the force of many cannot PerlMonks

### compare arrays, and list the difference

by iza (Monk)
 on Oct 03, 2012 at 10:29 UTC Need Help??
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 :-/

Replies are listed 'Best First'.
Re: compare arrays, and list the difference
by choroba (Bishop) 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;
لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: compare arrays, and list the difference
by BrowserUk (Pope) on Oct 03, 2012 at 11:03 UTC

For smallish integers only:

```#! 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.
Re: compare arrays, and list the difference
by Lotus1 (Curate) on Oct 03, 2012 at 13:12 UTC

List::Compare does exactly what you need and it is easy to use.

```    @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
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.

Which of FAQ4 answers the OPs question?

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.

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.

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 !

Re: compare arrays, and list the difference
by daxim (Chaplain) on Oct 03, 2012 at 10:42 UTC
These are set operations, see Set::Scalar or similar module.
Re: compare arrays, and list the difference
by Anonymous Monk on Oct 03, 2012 at 10:51 UTC

this is quite close but then I'd need to go throught one of the lists one more time :-/

I think you have to :)

```#!/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])

Re: compare arrays, and list the difference
by AnomalousMonk (Chancellor) 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]
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.
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. :-}
definitely - as usual, in perl, tmtowtdi :-)

Create A New User
Node Status?
node history
Node Type: perlquestion [id://997024]
Approved by marto
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (5)
As of 2018-02-24 20:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
When it is dark outside I am happiest to see ...

Results (310 votes). Check out past polls.

Notices?