Perl comparing two hashes

270 views Asked by At

I am a beginner with Perl, and am trying to write a script to compare the two hashes and print the values in the first hash that are not found in the second. Although I know the script should be very simple, I am not sure why mine is not working. Any help would be much appreciated.

My script so far:

#!/usr/bin/perl
use strict;
use warnings;
use vars qw($a $b $c $d $hash1 %hash1 $info1 $hash2 %hash2);

open (FILE1, "<file1.txt") || die "$!\n Couldn't open file1.txt\n";

while (<FILE1>){
    chomp (my $line=$_);
    my ($a, $b, $c, $d) = split (/\t/, $line); 
    if ($a){
         $hash1 -> {$a} -> {info1} = "$b\t$c\t$d"; 
       } 
        $info1=$hash1->{$a}->{info1};
 }



open (FILE2, "<file2.txt") || die "$!\n Couldnt open file2.txt \n";
open (Output, ">Output.txt")||die "Can't Open Output file";



while (<FILE2>) {
   chomp (my $line=$_);

   my ($a, $b, $c, $d) = split (/\t/, $line); 
   if ($a){
   $hash2 -> {$a} -> {info2} = "$b\t$c\t$d"; 
   } 


foreach (my $hash1->{$a}) {
    if (!exists $hash2{$a}) {
               print Output "$a\t$info1\n";
                      }
       }
 }

 close FILE1;
 close FILE2;
 close Output;

 print "Done!\n";
2

There are 2 answers

0
b4hand On BEST ANSWER

I've found what appears to be multiple bugs in your program. I've reformatted your code and left comments to the precise locations of the bugs below:

#!/usr/bin/env perl

use strict;
use warnings;

use autodie;

# use 3-arg open
open my $file1, '<', 'file1.txt'; # no need to check errors with autodie

# declare your lexical variables in the proper scope    
my $hash1 = {};
while (my $line = <$file1>) {
    chomp $line;
    my ($a, $b, $c, $d) = split /\t/, $line;
    if ($a) {
         $hash1->{$a}{info1} = "$b\t$c\t$d";
    }
    # It's unclear why you are assigning to this variable?
    my $info1 = $hash1->{$a}{info1};
}

open my $file2, '<', 'file2.txt';
open my $output, '>', 'Output.txt';

# same thing here: declare your lexical variables
my $hash2 = {};
while (my $line = <$file2>) {
    chomp $line;

    my ($a, $b, $c, $d) = split (/\t/, $line);
    if ($a) {
        $hash2->{$a}{info2} = "$b\t$c\t$d";
    }

    # BUG: You can't iterate over a hash directly, but you
    # can iterate over the keys of a hash.
    foreach my $key (keys %$hash1) {
        # BUG: You wrote `$hash2{$a}`, but probably meant `$hash2->{$a}`.
        if (!exists $hash2->{$key}) {
            # BUG: I think you probably meant for the following
            # `$info1` reference to refer to the value inside
            # `$hash1->{a}` and not the last line from the prior
            # loop. Using lexical variables will detect these
            # types of problems.
            my $info1 = $hash1->{$key}{info1};
            print {$output} "$key\t$info1\n";
        }
    }
}

# If you use lexical file handles, calling close is not required. They
# get closed at the end of the containing scope, which in this case is
# the end of the script.

print "Done!\n";
0
mpapec On

You can take all keys from %h1 and delete all keys from %h2, which leaves only keys which are not in %h2,

my %h1 = qw(k1 v1 k2 v2 k3 v3 k4 v4);
my %h2 = qw(k1 v1 k2 v2 k3 v3);

my %not_found_in_h2 = %h1;
delete @not_found_in_h2{keys %h2};

print "$_\n" for values %not_found_in_h2;

output

v4