Comparing two arrays and finding differences

Viewed 385

I need to compare two array and get differences.

Background:

1st array will list out files in folder.

2nd array will read the content of a file and stored in array.

The output of 1st array will be

a
b
c
d
e

The output of 2nd array will be

a
b
c
e

How I can compare those 2 array that get differences? The finale output that I want is

d

Here is the code:

#!/usr/bin/perl

use strict;
use warnings;

my $list  = "experiment.sv";
my $path  = "../../../folder1/";
my $filelist;

open ( OUTFILE, ">output.txt" );
main ();
close OUTFILE;


sub main {

   my @array1;
   opendir ( DIR, $path ) || die "Error in opening dir $path\n"; 
   while ( $filelist = readdir (DIR) ) {
       next if ( $filelist =~ s/\.//g);   #/
       push @array1, $filelist;         
   }
   closedir(DIR);

   my @array2;
   open( my $fh, "<", "$path/$list") or die "Failed to open file: $!\n";   
   while(<$fh>) { 
      push @array2, $_;                  
   } 
   close $fh;

   my @result;
   foreach my $array2 (@array2) {
       foreach my $array1 (@array1) {
           if ($array1 !~ /$array2/ ) {
               push @result, "$array1\n";
           }
       }
   }

   print OUTFILE "",@result;  

}
3 Answers

There are a few ways to do this, also depending on what exactly is needed.

Using an ancillary hash for each array, to reduce the existence check to a lookup

use warnings;
use strict;
use feature 'say';

sub diff_arys {
    my ($ra1, $ra2) = @_;

    my %in_a1 = map { $_ => 1 } @$ra1;
    my %in_a2 = map { $_ => 1 } @$ra2;

    my @not_in_one = grep { not exists $in_a1{$_} } @$ra2;
    my @not_in_two = grep { not exists $in_a2{$_} } @$ra1;

    return (@not_in_one ? \@not_in_one : undef), 
           (@not_in_two ? \@not_in_two : undef);
}

my @ary1 = 'a'..'e';         # a,b,c,d,e
my @ary2 = ('a'..'d', 'z');  # a,b,c,d, z
    
my ($not_in_one, $not_in_two) = diff_arys(\@ary1, \@ary2);

say "@$not_in_one"  if $not_in_one;
say "@$not_in_two"  if $not_in_two;

Prints

z
e

This finds difference both ways, elements in one array but not in the other. If you know for fact that you only need it for one "direction," to identify things that are in the first array but not in the second (as it seems from the question), then adjust the sub so to only get needed return. Then there's less code, and you can just return the array (so, a list or empty)

Note the choice for the interface: Return undef if no difference is found, an arrayref otherwise.

There are good modules for this kind of work. A rather comprehensive one is List::Compare. There are also Array::Utils and Array::Compare, and yet more. And then there are more complex tools that can also be used for this, like Algorithm::Diff.

As explained in perldoc -q "difference of", you can get the symmetric difference of two arrays like this:

my %count;
for my $x (@array1, @array2) {
    $count{$x}++;
}
my @difference;
for my $x (keys %count) {
    if ($count{$x} == 1) {
        push @difference, $x;
    }
}

This assumes there are no duplicate elements in either array.

Alternatively, if you only want to find elements of array1 that are not present in array2, you can build a hash from the second array and use that to filter the first array:

my %seen;
$seen{$_} = 1 for @array2;
my @missing = grep !$seen{$_}, @array1;

I'd do it like this:

use Data::Dumper;
my @array1= (qw(a b c d e f g   i  ));
my @array2= (qw(a b c   e   g h i j));

my %missing1, %missing2;

# Create an hash entry (value undef) for every array1 member
@missing1{@array1} = ();
# Delete every array2 member
delete @missing1{@array2};

# and v.v.
@missing2{@array2} = ();
delete @missing2{@array1};

print "Elements of array 1 missing in array 2:\n", Dumper sort keys %missing1;
print "Elements of array 2 missing in array 1:\n", Dumper sort keys %missing2;
Related