develooper Front page | perl.beginners | Postings from April 2010

Re: matching values of one hash to another

Thread Previous | Thread Next
From:
John W. Krahn
Date:
April 28, 2010 22:41
Subject:
Re: matching values of one hash to another
Message ID:
4BD91BF0.8040401@shaw.ca
Harry Putnam wrote:
> I need to do some matching of filenames in two top level directories.
> We expect to find a number of cases where the endnames ($_) are the
> same in both hierarchies but the full name is different.
> 
>   base1/my/file
>   base2/my/different_path/file
>  
> I've made hashes of the file names in two top level directories:
> 
> I've assembled the hashes using File::Find. We sort them first by
> matching the base directory to the top directories passed in.
> 
> My first impulse wast to do it by matching the endnames (or value) in
> one hash to any matching endname (value) in the other.  There are
> several more actions that follow (not coded yet) but That's a lot of
> spinning... 
> 
> I'm thinking there are better ways to do that.  So .. the script below
> does it the hard way, with all that spinning.  Can anyone suggest
> another way?
> 
> -------        ---------       ---=---       ---------      -------- 
> #!/usr/local/bin/perl
>   
> use strict;
> use warnings;
> 
> use File::Find;
> use Cwd;
> 
>  my $r1 = shift;
>  my $r2 = shift;

( my ( $r1, $r2 ) = @ARGV ) == 2
     or die "usage: $0 dir1 dir2\n";


>  my %r1h;
>  my %r2h;
> 
> my $r1hkcn = 0;
> my $r2hkcn = 0;
> 
> find(
>   sub {
>     ## For use in guaranteeing the -f command uses the
>     ## right path
>     my $dir = getcwd;

The current working directory is already in $File::Find::dir


>       if (-f $dir . '/'. $_) {

         return unless -f;


>         ## Determine if base dir matches r1 or r2
>         (my $base) = $File::Find::name =~ m/^(\.*\/*\/[^\/]+)\//;

Instead of all the "leaning toothpicks" use a different delimiter:

          (my $base) = $File::Find::name =~ m!^(\.*/*/[^/]+)/!;


>         if ($r1 eq $base) {
>           $r1h{$File::Find::name} = $_;
>           $r1hkcn++;
>           if ($r1hkcn == 1) {
>             print "v:$File::Find::name   k:$_\n";
>           }
>         } else {
>           $r2h{$File::Find::name} = $_;
>           $r2hkcn++;
>           if ($r2hkcn == 1) {
>             print "v:$File::Find::name   k:$_\n";
>           }
> 
>         }
>       }
>    },
>    $r1,$r2
> );
> 
> my ($r1full,$r1end);
> while (($r1full,$r1end) = each(%r1h)) {
>   foreach my $key (keys %r2h) {

No need for a loop there, just use exists():

     if ( exists $r2h{ $rlend } ) {
        print "$r2h{$rlend} MATCHES $r1end\n";


>     if ($r2h{$key} eq $r1end) {
>        print "$r2h{$key} MATCHES $r1end\n";
>     }
>   }
> }

Perhaps you want something like:

#!/usr/local/bin/perl
use strict;
use warnings;

use File::Find;

( my ( $r1, $r2 ) = @ARGV ) == 2
     or die "usage: $0 dir1 dir2\n";

my %data;

find sub {
     return unless -f;
     $data{ $_ }{ $r1 }++;
     }, $r1;

find sub {
     return unless -f;
     $data{ $_ }{ $r2 }++;
     }, $r2;

while ( my ( $file, $dir ) = each %data ) {
     my @dirs = keys %$dir;
     if ( @$dirs == 2 ) {
         print "$dirs[0] MATCHES $dirs[1]\n";
         }
     }

__END__



John
-- 
The programmer is fighting against the two most
destructive forces in the universe: entropy and
human stupidity.               -- Damian Conway

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About