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!

2
  • 1
    @mikeserv I fixed it!
    – user140071
    Oct 25, 2015 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, 2015 at 16:36

2 Answers 2

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
1
  • 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, 2015 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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .