2

I have three files, and I need to match the first column of file 1 to the first column of file 2 and then match the second column of file 1 with the first column of file 3.

  • file 1 contains the following lines plus many more:

    • fji01dde  AIDJFMGKG  
      dlp02sle  VMCFIJGM
      cmr03lsp  CKEIFJ
      
  • file 2 contains the following lines plus many more:

    • fji01dde  25 30  
      dlp02sle  40 50  
      cmr03lsp  60 70  
      
  • file 3 contains:

    • AIDJFMGKG  
      CKEIFJ  
      
  • my expected output is:

    • fji01dde AIDJFMGKG 25 30  
      cmr03lsp  CKEIFJ   60 70  
      
    • and so on...

  • I only want lines that are common in all three files, but when I run...

    • #!/usr/bin/env perl
      use strict;
      use warnings;
      my %data;
      
      while (<>) {  
          my ( $key, $value ) = split;  
          push( @{ $data{$key} }, $value );  
      }  
      
      foreach my $key ( sort keys %data ) {  
          if ( @{ $data{$key} } >= @ARGV ) {  
          print join( "\t", $key, @{ $data{$key} } ), "\n";  
          }    
      }
      
    • my results are...

    • AIDJFMGKG  
      CKEIFJ  
      fji01dde 25  
      dlp02sle 40  
      cmr03lsp 60
      

Any ideas? Thanks in advance!

  • 1
    @mikeserv I fixed it! – user140071 Oct 25 '15 at 5:10
  • Might be worth commenting next time, that you've cross posted to Perlmonks and Stack Overflow. Otherwise you might get 3 different people trying to solve the problem for you, and wasting their effort when you already have an answer. – Sobrique Oct 26 '15 at 16:36
2

The main problem with your script was that by the time the while(<>) loop has ended, @ARGV is empty. You need to get the arg count before the loop. And remember that perl arrays are zero-based so you have to subtract 1 from the count.

Here's a fixed version that produces the output you requested.

$ cat compare.pl 
#!/usr/bin/perl
use strict;
use warnings;

my $numargs=@ARGV-1;
my %data=();

while (<>) {  
    my ( $key, $value ) = split;  
    push( @{ $data{$key} }, $value );  

}  

foreach my $key ( sort keys %data ) {  
    if ( @{ $data{$key} } >= $numargs ) {  
    print join( "\t", $key, @{ $data{$key} } ), "\n";  
    }  
}

$ ./compare.pl file1 file2 file3
cmr03lsp    CKEIFJ  60
dlp02sle    VMCFIJGM    40
fji01dde    AIDJFMGKG   25
  • Doesn't quite match the OPs expected output - there's some extra trailing digits, and only two lines printed. I think that's because file3 has different "keys". As far as I can tell, that means excluding the line: dlp02sle VMCFIJGM because there's no element in the third file. – Sobrique Oct 26 '15 at 16:34
0

Crossposted question, therefore crossposted answer:

OK, looking at it - your problem is with that split - because by default, it splits on whitespace. Your second file has 3 fields by that yardstick, not two.

But also - you're not actually crossreferecing the same things, so your while ( <> ) { loop isn't going to do the trick.

  • In file 1 - you want to check for the value.
  • In file2, you're checking the key (and appending the values).
  • In file3, you have no value, just a key.

So with that in mind:

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;

#read file1 into a hash - but invert is it's value => key instead:
#          'CKEIFJ' => 'cmr03lsp',
# etc.  
open( my $file1, '<', "file1.txt" ) or die $!;
my %file1_content = map { reverse split } <$file1>;
close($file1);

print Dumper \%file1_content;

#read file 2 - read keys, store the values. 
#split _2_ fields, so we keep both numbers as a substring:
#e.g.:
#          'cmr03lsp' => '60 70
#',

open( my $file2, '<', "file2.txt" ) or die $!;
my %file2_content = map { split( " ", $_, 2 ) } <$file2>;
close($file2);

print Dumper \%file2_content;    

#then iterate file 3, checking if:
#file1 has a matching 'key' (but inverted - as a value) 
#file2 has a cross reference. 
open( my $file3, '<', "file3.txt" ) or die $!;
while ( my $line = <$file3> ) {
    chomp $line;
    if (    $file1_content{$line}
        and $file2_content{ $file1_content{$line} } )
    {
        print
            "$file1_content{$line} $line $file2_content{$file1_content{$line}}";
    }
}
close($file3);

This prints (excluding the "dumper" output):

fji01dde AIDJFMGKG 25 30
cmr03lsp CKEIFJ 60 70

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.