Just another Perl shrine PerlMonks

### Difference between two arrays - is there a better way?

by blindluke (Hermit)
 on Aug 09, 2011 at 08:58 UTC Need Help??

blindluke has asked for the wisdom of the Perl Monks concerning the following question:

Hello, enlightened Monks!

A few days ago, a colleague of mine faced a simple problem - he had a list containing all users, and another one, containing disabled users. He wanted to find active users.

This, of course is a problem of finding a difference between two arrays, and perlfaq4 shows a simple way of solving it. This is my initial solution, based upon the perlfaq info:

```#!/usr/bin/perl -w
use strict;

my @list1 = (1, 2, 3, 4, 5);
my @list2 = (2, 3, 4);

my @diff;
my %repeats;

for (@list1, @list2) { \$repeats{\$_}++ }
for (keys %repeats) {
push @diff, \$_ unless \$repeats{\$_} > 1;
}

A short while after I send him this code, my colleague found another way of solving the problem. This is his approach:

```#!/usr/bin/perl -w
use strict;

my @list1 = (1, 2, 3, 4, 5);
my @list2 = (2, 3, 4);

my %diff;

@diff{ @list1 } = undef;
delete @diff{ @list2 };

I'm wondering, is there another clever way of getting the difference between two arrays? Could it be done in some way using map or grep?

The second array is a subset of the first one, so this is not a general case of computing a difference between arrays.

Regards,
Luke

Replies are listed 'Best First'.
Re: Difference between two arrays - is there a better way?
by BrowserUk (Pope) on Aug 09, 2011 at 09:06 UTC
is there another clever way

Another way for sure. Whether it is "clever"?

```@list1 = (1, 2, 3, 4, 5);
@list2 = (2, 3, 4);;
@diff{ @list2 }= ();;

print grep !exists(\$diff{\$_}), @list1;;
1 5

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: Difference between two arrays - is there a better way?
by JavaFan (Canon) on Aug 09, 2011 at 09:22 UTC
Your initial solution relies on users not appearing twice in a list. And that there's no user listed only in the disabled users. I very much prefer the solution of your coworker, which does not rely on these dependencies.

And if you insist on a map/grep solution:

```my %disabled = map {(\$_, 1)} @list2;
my @active = grep {!\$disabled{\$_}} @list1;
This solution doesn't rely on the dependencies mentioned above either.

Thanks.

Both dependencies will always be met in this particular case - as I mentioned, this was not a general problem.

Still, I also prefer the second solution - it's not only more general than mine, but looks better, and seems more simple/elegant (although the aesthetic argument may not be as valid as the one you mentioned - that it is more general).

I was simply wondering about other ways, in the spirit of TMTOWTDI. Thank you, and thanks to BrowserUk for both other solutions.

Re: Difference between two arrays - is there a better way?
by bart (Canon) on Aug 09, 2011 at 11:11 UTC
Here is my own "clever way", and you can even use it on more than 2 lists.
```my %presence;
my \$b = 1;
foreach my \$ary (\@list1, \@list2, \@list3) {
foreach(@\$ary) {
\$presence{\$_} |= \$b;
}
} continue {
\$b *= 2;
}
Now you can grep in keys %presence. Each value will indicate in what lists the item was found, as a bit mask. Examples:
```@list1only = grep { \$presence{\$_} == 1 } keys %presence;
@list2only = grep { \$presence{\$_} == 2 } keys %presence;
@list3only = grep { \$presence{\$_} == 4 } keys %presence;
@lists1and2only = grep { \$presence{\$_} == 1+2 } keys %presence;
@inall = grep { \$presence{\$_} == 1+2+4 } keys %presence;
# etc
Re: Difference between two arrays - is there a better way?
by Khen1950fx (Canon) on Aug 09, 2011 at 12:45 UTC
Instead of a diff, try finding unique users---the users who are still active. Using List::Compare:
```#!/usr/bin/perl

use strict;
use warnings;
use List::Compare;
use Data::Dump qw(dump);

my @list1 = qw(1 2 3 4 5);
my @list2 = qw(2 3 4);

my \$lc = List::Compare->new(\@list1, \@list2);
print "Disabled users: ", dump(\$lc->get_intersection), "\n";
print "Active users: ", dump(\$lc->get_unique), "\n";
Re: Difference between two arrays - is there a better way?
by stonecolddevin (Parson) on Aug 09, 2011 at 19:30 UTC
Re: Difference between two arrays - is there a better way?
by SimonSaysCake (Beadle) on Dec 04, 2014 at 16:57 UTC

This one-liner (versus answers with hashes and inspired by another answer) is working for me but has not been very rigorously tested (as run on AS Perl 5.16.3):

```@foo = (1, 2, 3, 4, 5, 6, 7,  8,  9,  10);
@bar = (0, 1, 2, 5, 7, 9, 11, 12, 13, 14);
my @diff = grep {my \$baz = \$_; ! grep(\$_ == \$baz, @bar)} @foo;
print join(",", @diff);

Gives:
3,4,6,8,10

Switch the lists around in the nested greps to go from being "in foo but not also bar" to "in bar but not also foo".

-Simon

wow this is mind-bending! can you please go through it in detail?
```@cities = (qw(London Oslo Paris Amsterdam Berlin ));
@visited = (qw(Berlin Oslo));

say "Still need to visit:",
grep { ! ({ \$_, 0 } ~~ @visited) } @cities;
What if the 2 lists are array of hashes?

Then I recommend you adapt the concept in perlfaq4 to your situation.

Create A New User
Node Status?
node history
Node Type: perlquestion [id://919422]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (9)
As of 2021-01-22 15:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
The STEM quote I most wish I'd made is:

Results (239 votes). Check out past polls.

Notices?